Skip to content

Commit 72e6059

Browse files
authored
feat(cubesql): Push Limit-Sort down Projection (#9776)
1 parent 93c83f8 commit 72e6059

File tree

5 files changed

+304
-22
lines changed

5 files changed

+304
-22
lines changed

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17168,4 +17168,59 @@ LIMIT {{ limit }}{% endif %}"#.to_string(),
1716817168
}
1716917169
)
1717017170
}
17171+
17172+
#[tokio::test]
17173+
async fn test_push_down_limit_sort_projection() {
17174+
init_testing_logger();
17175+
17176+
let logical_plan = convert_select_to_query_plan(
17177+
r#"
17178+
SELECT
17179+
"ta_1"."customer_gender" AS "ca_1",
17180+
DATE_TRUNC('MONTH', CAST("ta_1"."order_date" AS date)) AS "ca_2",
17181+
COALESCE(sum("ta_1"."sumPrice"), 0) AS "ca_3"
17182+
FROM
17183+
"db"."public"."KibanaSampleDataEcommerce" AS "ta_1"
17184+
WHERE
17185+
(
17186+
"ta_1"."order_date" >= TIMESTAMP '2024-01-01 00:00:00.0'
17187+
AND "ta_1"."order_date" < TIMESTAMP '2025-01-01 00:00:00.0'
17188+
)
17189+
GROUP BY
17190+
"ca_1",
17191+
"ca_2"
17192+
ORDER BY
17193+
"ca_2" ASC NULLS LAST
17194+
LIMIT
17195+
5000
17196+
;"#
17197+
.to_string(),
17198+
DatabaseProtocol::PostgreSQL,
17199+
)
17200+
.await
17201+
.as_logical_plan();
17202+
17203+
assert_eq!(
17204+
logical_plan.find_cube_scan().request,
17205+
V1LoadRequestQuery {
17206+
measures: Some(vec!["KibanaSampleDataEcommerce.sumPrice".to_string()]),
17207+
dimensions: Some(vec!["KibanaSampleDataEcommerce.customer_gender".to_string()]),
17208+
segments: Some(vec![]),
17209+
time_dimensions: Some(vec![V1LoadRequestQueryTimeDimension {
17210+
dimension: "KibanaSampleDataEcommerce.order_date".to_string(),
17211+
granularity: Some("month".to_string()),
17212+
date_range: Some(json!(vec![
17213+
"2024-01-01T00:00:00.000Z".to_string(),
17214+
"2024-12-31T23:59:59.999Z".to_string()
17215+
])),
17216+
},]),
17217+
order: Some(vec![vec![
17218+
"KibanaSampleDataEcommerce.order_date".to_string(),
17219+
"asc".to_string(),
17220+
]]),
17221+
limit: Some(5000),
17222+
..Default::default()
17223+
}
17224+
)
17225+
}
1717117226
}

rust/cubesql/cubesql/src/compile/rewrite/cost.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@ impl BestCubePlan {
5151
_ => 0,
5252
};
5353

54-
let non_pushed_down_limit_sort = match enode {
55-
LogicalPlanLanguage::Sort(_) => 1,
56-
_ => 0,
57-
};
58-
5954
let ast_size_inside_wrapper = match enode {
6055
LogicalPlanLanguage::WrappedSelect(_) => 1,
6156
_ => 0,
@@ -130,6 +125,8 @@ impl BestCubePlan {
130125
LogicalPlanLanguage::JoinCheckStage(_) => 1,
131126
LogicalPlanLanguage::JoinCheckPushDown(_) => 1,
132127
LogicalPlanLanguage::JoinCheckPullUp(_) => 1,
128+
LogicalPlanLanguage::SortProjectionPushdownReplacer(_) => 1,
129+
LogicalPlanLanguage::SortProjectionPullupReplacer(_) => 1,
133130
// Not really replacers but those should be deemed as mandatory rewrites and as soon as
134131
// there's always rewrite rule it's fine to have replacer cost.
135132
// Needs to be added as alias rewrite always more expensive than original function.
@@ -220,7 +217,8 @@ impl BestCubePlan {
220217
member_errors,
221218
non_pushed_down_window,
222219
non_pushed_down_grouping_sets,
223-
non_pushed_down_limit_sort,
220+
// Will be filled in finalize
221+
non_pushed_down_limit_sort: 0,
224222
zero_members_wrapper,
225223
cube_members,
226224
errors: this_errors,
@@ -405,9 +403,8 @@ impl CubePlanCost {
405403
CubePlanState::Wrapper => 0,
406404
},
407405
non_pushed_down_limit_sort: match sort_state {
408-
SortState::DirectChild => self.non_pushed_down_limit_sort,
409-
SortState::Current => self.non_pushed_down_limit_sort,
410-
_ => 0,
406+
SortState::Current => self.non_pushed_down_limit_sort + 1,
407+
_ => self.non_pushed_down_limit_sort,
411408
},
412409
// Don't track state here: we want representation that have fewer wrappers with zero members _in total_
413410
zero_members_wrapper: self.zero_members_wrapper,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,13 @@ crate::plan_to_language! {
508508
members: Vec<LogicalPlan>,
509509
alias_to_cube: Vec<(String, String)>,
510510
},
511+
SortProjectionPushdownReplacer {
512+
expr: Arc<Expr>,
513+
column_to_expr: Vec<(Column, Expr)>,
514+
},
515+
SortProjectionPullupReplacer {
516+
expr: Arc<Expr>,
517+
},
511518
EventNotification {
512519
name: String,
513520
members: Vec<LogicalPlan>,
@@ -2236,6 +2243,17 @@ fn join_check_pull_up(expr: impl Display, left: impl Display, right: impl Displa
22362243
format!("(JoinCheckPullUp {expr} {left} {right})")
22372244
}
22382245

2246+
fn sort_projection_pushdown_replacer(expr: impl Display, column_to_expr: impl Display) -> String {
2247+
format!(
2248+
"(SortProjectionPushdownReplacer {} {})",
2249+
expr, column_to_expr
2250+
)
2251+
}
2252+
2253+
fn sort_projection_pullup_replacer(expr: impl Display) -> String {
2254+
format!("(SortProjectionPullupReplacer {})", expr)
2255+
}
2256+
22392257
pub fn original_expr_name(egraph: &CubeEGraph, id: Id) -> Option<String> {
22402258
egraph[id]
22412259
.data

rust/cubesql/cubesql/src/compile/rewrite/rewriter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ impl Rewriter {
477477
eval_stable_functions,
478478
),
479479
&DateRules::new(config_obj.clone()),
480-
&OrderRules::new(),
480+
&OrderRules::new(config_obj.clone()),
481481
&CommonRules::new(config_obj.clone()),
482482
];
483483
let mut rewrites = Vec::new();

0 commit comments

Comments
 (0)