Skip to content

Commit fc129d4

Browse files
authored
feat(cubesql): PowerBI support for wrapped queries (#4752)
* feat(cubesql): PowerBI support for wrapped queries * chore(cubesql): PowerBI support for wrapped queries -- support filters for aliased fields * chore(cubesql): PowerBI support for wrapped queries -- replace column alias replacer with member pushdown replacer for centralized aliasing * chore(cubesql): PowerBI support for wrapped queries -- remove unused replacers * chore(cubesql): PowerBI support for wrapped queries -- list concat replacer for flattening old members search
1 parent aae394c commit fc129d4

File tree

12 files changed

+1560
-1037
lines changed

12 files changed

+1560
-1037
lines changed

Diff for: rust/cubesql/cubesql/src/compile/engine/df/scan.rs

+144-116
Large diffs are not rendered by default.

Diff for: rust/cubesql/cubesql/src/compile/mod.rs

+141-25
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ impl QueryPlanner {
16301630
schema
16311631
.fields()
16321632
.iter()
1633-
.map(|f| f.name().to_string())
1633+
.map(|f| Some(f.name().to_string()))
16341634
.collect(),
16351635
query.request,
16361636
// @todo Remove after split!
@@ -3597,7 +3597,13 @@ mod tests {
35973597
order: None,
35983598
limit: None,
35993599
offset: None,
3600-
filters: None,
3600+
filters: Some(vec![V1LoadRequestQueryFilterItem {
3601+
member: Some("KibanaSampleDataEcommerce.count".to_string()),
3602+
operator: Some("gt".to_string()),
3603+
values: Some(vec!["0".to_string()]),
3604+
or: None,
3605+
and: None,
3606+
}]),
36013607
}
36023608
);
36033609

@@ -3617,13 +3623,22 @@ mod tests {
36173623
order: None,
36183624
limit: None,
36193625
offset: None,
3620-
filters: Some(vec![V1LoadRequestQueryFilterItem {
3621-
member: Some("KibanaSampleDataEcommerce.has_subscription".to_string()),
3622-
operator: Some("equals".to_string()),
3623-
values: Some(vec!["false".to_string()]),
3624-
or: None,
3625-
and: None,
3626-
}]),
3626+
filters: Some(vec![
3627+
V1LoadRequestQueryFilterItem {
3628+
member: Some("KibanaSampleDataEcommerce.has_subscription".to_string()),
3629+
operator: Some("equals".to_string()),
3630+
values: Some(vec!["false".to_string()]),
3631+
or: None,
3632+
and: None,
3633+
},
3634+
V1LoadRequestQueryFilterItem {
3635+
member: Some("KibanaSampleDataEcommerce.count".to_string()),
3636+
operator: Some("gt".to_string()),
3637+
values: Some(vec!["0".to_string()]),
3638+
or: None,
3639+
and: None,
3640+
}
3641+
]),
36273642
}
36283643
);
36293644
}
@@ -3884,7 +3899,13 @@ ORDER BY \"COUNT(count)\" DESC"
38843899
order: None,
38853900
limit: None,
38863901
offset: None,
3887-
filters: None,
3902+
filters: Some(vec![V1LoadRequestQueryFilterItem {
3903+
member: Some("KibanaSampleDataEcommerce.count".to_string()),
3904+
operator: Some("gt".to_string()),
3905+
values: Some(vec!["0".to_string()]),
3906+
or: None,
3907+
and: None,
3908+
}]),
38883909
}
38893910
);
38903911

@@ -3895,17 +3916,11 @@ ORDER BY \"COUNT(count)\" DESC"
38953916
.iter()
38963917
.map(|f| f.name().to_string())
38973918
.collect::<Vec<_>>(),
3898-
vec![
3899-
"SUM(KibanaSampleDataEcommerce.count)".to_string(),
3900-
"COUNT(UInt8(1))".to_string()
3901-
]
3919+
vec!["sum:count:ok".to_string(),]
39023920
);
39033921
assert_eq!(
39043922
&cube_scan.member_fields,
3905-
&vec![
3906-
"KibanaSampleDataEcommerce.count".to_string(),
3907-
"KibanaSampleDataEcommerce.count".to_string()
3908-
]
3923+
&vec![Some("KibanaSampleDataEcommerce.count".to_string())]
39093924
);
39103925
}
39113926

@@ -4113,7 +4128,7 @@ ORDER BY \"COUNT(count)\" DESC"
41134128

41144129
let query_plan = convert_select_to_query_plan(
41154130
"select \"rows\".\"customer_gender\" as \"customer_gender\",
4116-
\n count(1) as \"a0\"\
4131+
\n sum(\"rows\".\"count\") as \"a0\"\
41174132
\nfrom\
41184133
\n(\
41194134
\n select \"_\".\"count\",\
@@ -4140,7 +4155,7 @@ ORDER BY \"COUNT(count)\" DESC"
41404155
segments: Some(vec![]),
41414156
time_dimensions: None,
41424157
order: None,
4143-
limit: Some(1000001),
4158+
limit: Some(50000),
41444159
offset: None,
41454160
filters: Some(vec![V1LoadRequestQueryFilterItem {
41464161
member: Some("KibanaSampleDataEcommerce.customer_gender".to_string()),
@@ -4153,6 +4168,108 @@ ORDER BY \"COUNT(count)\" DESC"
41534168
);
41544169
}
41554170

4171+
#[test]
4172+
fn powerbi_inner_wrapped_dates() {
4173+
init_logger();
4174+
4175+
let query_plan = convert_select_to_query_plan(
4176+
"select \"_\".\"created_at_day\",\
4177+
\n \"_\".\"a0\"\
4178+
\nfrom \
4179+
\n(\
4180+
\n select \"rows\".\"created_at_day\" as \"created_at_day\",\
4181+
\n sum(\"rows\".\"cnt\") as \"a0\"\
4182+
\n from \
4183+
\n (\
4184+
\n select count(*) cnt,date_trunc('day', order_date) as created_at_day, date_trunc('month', order_date) as created_at_month from public.KibanaSampleDataEcommerce group by 2, 3\
4185+
\n ) \"rows\"\
4186+
\n group by \"created_at_day\"\
4187+
\n) \"_\"\
4188+
\nwhere not \"_\".\"a0\" is null\
4189+
\nlimit 1000001"
4190+
.to_string(),
4191+
DatabaseProtocol::PostgreSQL,
4192+
);
4193+
4194+
let logical_plan = query_plan.as_logical_plan();
4195+
assert_eq!(
4196+
logical_plan.find_cube_scan().request,
4197+
V1LoadRequestQuery {
4198+
measures: Some(vec!["KibanaSampleDataEcommerce.count".to_string()]),
4199+
dimensions: Some(vec![]),
4200+
segments: Some(vec![]),
4201+
time_dimensions: Some(vec![V1LoadRequestQueryTimeDimension {
4202+
dimension: "KibanaSampleDataEcommerce.order_date".to_string(),
4203+
granularity: Some("day".to_string()),
4204+
date_range: None,
4205+
}]),
4206+
order: None,
4207+
limit: Some(50000),
4208+
offset: None,
4209+
filters: Some(vec![V1LoadRequestQueryFilterItem {
4210+
member: Some("KibanaSampleDataEcommerce.count".to_string()),
4211+
operator: Some("set".to_string()),
4212+
values: None,
4213+
or: None,
4214+
and: None,
4215+
}]),
4216+
}
4217+
);
4218+
}
4219+
4220+
#[test]
4221+
fn powerbi_inner_wrapped_asterisk() {
4222+
init_logger();
4223+
4224+
let query_plan = convert_select_to_query_plan(
4225+
"select \"rows\".\"customer_gender\" as \"customer_gender\",\
4226+
\n \"rows\".\"created_at_month\" as \"created_at_month\"\
4227+
\nfrom \
4228+
\n(\
4229+
\n select \"_\".\"count\",\
4230+
\n \"_\".\"minPrice\",\
4231+
\n \"_\".\"maxPrice\",\
4232+
\n \"_\".\"avgPrice\",\
4233+
\n \"_\".\"order_date\",\
4234+
\n \"_\".\"customer_gender\",\
4235+
\n \"_\".\"created_at_day\",\
4236+
\n \"_\".\"created_at_month\"\
4237+
\n from \
4238+
\n (\
4239+
\n select *, date_trunc('day', order_date) created_at_day, date_trunc('month', order_date) created_at_month from public.KibanaSampleDataEcommerce\
4240+
\n ) \"_\"\
4241+
\n where \"_\".\"created_at_month\" < timestamp '2022-06-13 00:00:00' and \"_\".\"created_at_month\" >= timestamp '2021-12-16 00:00:00'\
4242+
\n) \"rows\"\
4243+
\ngroup by \"customer_gender\",\
4244+
\n \"created_at_month\"\
4245+
\nlimit 1000001"
4246+
.to_string(),
4247+
DatabaseProtocol::PostgreSQL,
4248+
);
4249+
4250+
let logical_plan = query_plan.as_logical_plan();
4251+
assert_eq!(
4252+
logical_plan.find_cube_scan().request,
4253+
V1LoadRequestQuery {
4254+
measures: Some(vec![]),
4255+
dimensions: Some(vec!["KibanaSampleDataEcommerce.customer_gender".to_string()]),
4256+
segments: Some(vec![]),
4257+
time_dimensions: Some(vec![V1LoadRequestQueryTimeDimension {
4258+
dimension: "KibanaSampleDataEcommerce.order_date".to_string(),
4259+
granularity: Some("month".to_string()),
4260+
date_range: Some(json!(vec![
4261+
"2021-12-16 00:00:00".to_string(),
4262+
"2022-06-13 00:00:00".to_string()
4263+
])),
4264+
}]),
4265+
order: None,
4266+
limit: Some(50000),
4267+
offset: None,
4268+
filters: None,
4269+
}
4270+
);
4271+
}
4272+
41564273
#[test]
41574274
fn test_select_aggregations() {
41584275
let variants = vec![
@@ -4304,10 +4421,10 @@ ORDER BY \"COUNT(count)\" DESC"
43044421
// "SELECT is_male FROM KibanaSampleDataEcommerce".to_string(),
43054422
// CompilationError::User("Unable to use segment 'is_male' as column in SELECT statement".to_string()),
43064423
// ),
4307-
(
4308-
"SELECT COUNT(*) FROM KibanaSampleDataEcommerce GROUP BY is_male".to_string(),
4309-
CompilationError::User("Unable to use segment 'is_male' in GROUP BY".to_string()),
4310-
),
4424+
// (
4425+
// "SELECT COUNT(*) FROM KibanaSampleDataEcommerce GROUP BY is_male".to_string(),
4426+
// CompilationError::User("Unable to use segment 'is_male' in GROUP BY".to_string()),
4427+
// ),
43114428
// (
43124429
// "SELECT COUNT(*) FROM KibanaSampleDataEcommerce ORDER BY is_male DESC".to_string(),
43134430
// CompilationError::User("Unable to use segment 'is_male' in ORDER BY".to_string()),
@@ -4617,7 +4734,6 @@ ORDER BY \"COUNT(count)\" DESC"
46174734
""
46184735
}
46194736
);
4620-
println!("Query: {}", query);
46214737
let logical_plan =
46224738
convert_select_to_query_plan(query, DatabaseProtocol::MySQL).as_logical_plan();
46234739

Diff for: rust/cubesql/cubesql/src/compile/rewrite/analysis.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
rewrite::{
55
converter::{is_expr_node, node_to_expr},
66
AliasExprAlias, ColumnExprColumn, DimensionName, LiteralExprValue, LogicalPlanLanguage,
7-
MeasureName, TableScanSourceTableName, TimeDimensionName,
7+
MeasureName, SegmentName, TableScanSourceTableName, TimeDimensionName,
88
},
99
},
1010
var_iter, CubeError,
@@ -124,6 +124,16 @@ impl LogicalPlanAnalysis {
124124
None
125125
}
126126
}
127+
LogicalPlanLanguage::Segment(params) => {
128+
if let Some(_) = column_name(params[1]) {
129+
let expr = original_expr(params[1])?;
130+
let segment_name = var_iter!(egraph[params[0]], SegmentName).next().unwrap();
131+
map.push((segment_name.to_string(), expr));
132+
Some(map)
133+
} else {
134+
None
135+
}
136+
}
127137
LogicalPlanLanguage::TimeDimension(params) => {
128138
if let Some(_) = column_name(params[3]) {
129139
let expr = original_expr(params[3])?;

0 commit comments

Comments
 (0)