Skip to content

Commit 2e85182

Browse files
authored
feat(cubesql): Support LOWER(?column) IN (?literal) (cube-js#5319)
1 parent d47987e commit 2e85182

File tree

2 files changed

+87
-35
lines changed

2 files changed

+87
-35
lines changed

rust/cubesql/cubesql/src/compile/mod.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,8 @@ mod tests {
16891689

16901690
#[tokio::test]
16911691
async fn test_select_compound_identifiers() {
1692+
init_logger();
1693+
16921694
let query_plan = convert_select_to_query_plan(
16931695
"SELECT MEASURE(`KibanaSampleDataEcommerce`.`maxPrice`) AS maxPrice, MEASURE(`KibanaSampleDataEcommerce`.`minPrice`) AS minPrice FROM KibanaSampleDataEcommerce".to_string(), DatabaseProtocol::MySQL
16941696
).await;
@@ -1714,6 +1716,8 @@ mod tests {
17141716

17151717
#[tokio::test]
17161718
async fn test_select_measure_aggregate_functions() {
1719+
init_logger();
1720+
17171721
let query_plan = convert_select_to_query_plan(
17181722
"SELECT MAX(maxPrice), MIN(minPrice), AVG(avgPrice) FROM KibanaSampleDataEcommerce"
17191723
.to_string(),
@@ -1753,6 +1757,8 @@ mod tests {
17531757

17541758
#[tokio::test]
17551759
async fn test_change_user_via_filter() {
1760+
init_logger();
1761+
17561762
let query_plan = convert_select_to_query_plan(
17571763
"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce WHERE __user = 'gopher'"
17581764
.to_string(),
@@ -1781,6 +1787,8 @@ mod tests {
17811787

17821788
#[tokio::test]
17831789
async fn test_change_user_via_in_filter() {
1790+
init_logger();
1791+
17841792
let query_plan = convert_select_to_query_plan(
17851793
"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce WHERE __user IN ('gopher')"
17861794
.to_string(),
@@ -1809,6 +1817,8 @@ mod tests {
18091817

18101818
#[tokio::test]
18111819
async fn test_starts_with() {
1820+
init_logger();
1821+
18121822
let query_plan = convert_select_to_query_plan(
18131823
"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce WHERE starts_with(customer_gender, 'fe')"
18141824
.to_string(),
@@ -1841,6 +1851,8 @@ mod tests {
18411851

18421852
#[tokio::test]
18431853
async fn test_ends_with_query() {
1854+
init_logger();
1855+
18441856
let query_plan = convert_select_to_query_plan(
18451857
"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce WHERE ends_with(customer_gender, 'emale')"
18461858
.to_string(),
@@ -1871,8 +1883,53 @@ mod tests {
18711883
)
18721884
}
18731885

1886+
#[tokio::test]
1887+
async fn test_lower_in_thoughtspot() {
1888+
init_logger();
1889+
1890+
let query_plan = convert_select_to_query_plan(
1891+
"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce WHERE LOWER(customer_gender) IN ('female')"
1892+
.to_string(),
1893+
DatabaseProtocol::PostgreSQL,
1894+
)
1895+
.await;
1896+
let cube_scan = query_plan.as_logical_plan().find_cube_scan();
1897+
1898+
assert_eq!(
1899+
cube_scan.request,
1900+
V1LoadRequestQuery {
1901+
measures: Some(vec!["KibanaSampleDataEcommerce.count".to_string(),]),
1902+
segments: Some(vec![]),
1903+
dimensions: Some(vec![]),
1904+
time_dimensions: None,
1905+
order: None,
1906+
limit: None,
1907+
offset: None,
1908+
// TODO: Migrate to equalsLower operator, when it will be available in Cube?
1909+
filters: Some(vec![
1910+
V1LoadRequestQueryFilterItem {
1911+
member: Some("KibanaSampleDataEcommerce.customer_gender".to_string()),
1912+
operator: Some("startsWith".to_string()),
1913+
values: Some(vec!["female".to_string()]),
1914+
or: None,
1915+
and: None
1916+
},
1917+
V1LoadRequestQueryFilterItem {
1918+
member: Some("KibanaSampleDataEcommerce.customer_gender".to_string()),
1919+
operator: Some("endsWith".to_string()),
1920+
values: Some(vec!["female".to_string()]),
1921+
or: None,
1922+
and: None
1923+
}
1924+
])
1925+
}
1926+
)
1927+
}
1928+
18741929
#[tokio::test]
18751930
async fn test_lower_equals_thoughtspot() {
1931+
init_logger();
1932+
18761933
let query_plan = convert_select_to_query_plan(
18771934
"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce WHERE LOWER(customer_gender) = 'female'"
18781935
.to_string(),
@@ -1915,6 +1972,8 @@ mod tests {
19151972

19161973
#[tokio::test]
19171974
async fn test_change_user_via_in_filter_thoughtspot() {
1975+
init_logger();
1976+
19181977
let query_plan = convert_select_to_query_plan(
19191978
r#"SELECT COUNT(*) as cnt FROM KibanaSampleDataEcommerce "ta_1" WHERE (LOWER("ta_1"."__user") IN ('gopher')) = TRUE"#.to_string(),
19201979
DatabaseProtocol::PostgreSQL,

rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -264,40 +264,43 @@ impl RewriteRules for FilterRules {
264264
),
265265
),
266266
transforming_rewrite(
267-
"change-user-equal-filter",
267+
"change-user-lower-equal-filter",
268268
filter_replacer(
269-
binary_expr(column_expr("?column"), "=", literal_expr("?literal")),
269+
binary_expr(
270+
fun_expr("Lower", vec![column_expr("?column")]),
271+
"=",
272+
literal_expr("?literal"),
273+
),
270274
"?alias_to_cube",
271275
"?members",
272276
),
273277
change_user_member("?user"),
274278
self.transform_change_user_eq("?column", "?literal", "?user"),
275279
),
276-
// ?change_user IN (?list..)
277280
transforming_rewrite(
278-
"change-user-in-filter",
281+
"change-user-equal-filter",
279282
filter_replacer(
280-
inlist_expr(column_expr("?column"), "?list", "?negated"),
283+
binary_expr(column_expr("?column"), "=", literal_expr("?literal")),
281284
"?alias_to_cube",
282285
"?members",
283286
),
284287
change_user_member("?user"),
285-
self.transform_change_user_in("?column", "?list", "?negated", "?user"),
288+
self.transform_change_user_eq("?column", "?literal", "?user"),
286289
),
287-
// LOWER(?change_user) IN (?list..) - It's not safety to replace LOWER fn for all filters
290+
// ?expr IN (?one_element..)
288291
transforming_rewrite(
289-
"change-user-lower-in-filter",
292+
"in-filter-equal",
290293
filter_replacer(
291-
inlist_expr(
292-
fun_expr("Lower", vec![column_expr("?column")]),
293-
"?list",
294-
"?negated",
295-
),
294+
inlist_expr("?expr", "?list", "?negated"),
296295
"?alias_to_cube",
297296
"?members",
298297
),
299-
change_user_member("?user"),
300-
self.transform_change_user_in("?column", "?list", "?negated", "?user"),
298+
filter_replacer(
299+
binary_expr("?expr", "=", literal_expr("?literal")),
300+
"?alias_to_cube",
301+
"?members",
302+
),
303+
self.transform_filter_in_to_equal("?list", "?negated", "?literal"),
301304
),
302305
rewrite(
303306
"filter-in-place-filter-to-true-filter",
@@ -1776,17 +1779,16 @@ impl FilterRules {
17761779
}
17771780
}
17781781

1779-
fn transform_change_user_in(
1782+
// Transform ?expr IN (?literal) to ?expr = ?literal
1783+
fn transform_filter_in_to_equal(
17801784
&self,
1781-
column_var: &'static str,
17821785
list_var: &'static str,
17831786
negated_var: &'static str,
1784-
change_user_member_var: &'static str,
1787+
return_literal_var: &'static str,
17851788
) -> impl Fn(&mut EGraph<LogicalPlanLanguage, LogicalPlanAnalysis>, &mut Subst) -> bool {
1786-
let column_var = var!(column_var);
17871789
let list_var = var!(list_var);
17881790
let negated_var = var!(negated_var);
1789-
let change_user_member_var = change_user_member_var.parse().unwrap();
1791+
let return_literal_var = var!(return_literal_var);
17901792

17911793
move |egraph, subst| {
17921794
for negated in var_iter!(egraph[subst[negated_var]], InListExprNegated) {
@@ -1799,23 +1801,14 @@ impl FilterRules {
17991801
return false;
18001802
}
18011803

1802-
if let Some(ScalarValue::Utf8(Some(change_user))) = list.first() {
1803-
let specified_user = change_user.clone();
1804+
if let Some(scalar) = list.first().cloned() {
1805+
let first_scalar = egraph.add(LogicalPlanLanguage::LiteralExprValue(
1806+
LiteralExprValue(scalar),
1807+
));
18041808

1805-
for column in
1806-
var_iter!(egraph[subst[column_var]], ColumnExprColumn).cloned()
1807-
{
1808-
if column.name.eq_ignore_ascii_case("__user") {
1809-
subst.insert(
1810-
change_user_member_var,
1811-
egraph.add(LogicalPlanLanguage::ChangeUserMemberValue(
1812-
ChangeUserMemberValue(specified_user),
1813-
)),
1814-
);
1809+
subst.insert(return_literal_var, first_scalar);
18151810

1816-
return true;
1817-
}
1818-
}
1811+
return true;
18191812
}
18201813
}
18211814
}

0 commit comments

Comments
 (0)