Skip to content

Commit b9c97cd

Browse files
authored
feat(cubesql): Push down DATE_TRUNC expressions as member expressions with granularity (#9583)
1 parent 2e47147 commit b9c97cd

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
extract_exprlist_from_groupping_set,
99
rules::{
1010
filters::Decimal,
11-
utils::{DecomposedDayTime, DecomposedMonthDayNano},
11+
utils::{granularity_str_to_int_order, DecomposedDayTime, DecomposedMonthDayNano},
1212
},
1313
LikeType, WrappedSelectType,
1414
},
@@ -2520,6 +2520,46 @@ impl WrappedSelectNode {
25202520
))
25212521
}
25222522
Expr::ScalarFunction { fun, args } => {
2523+
if args.len() == 2 {
2524+
if let (
2525+
BuiltinScalarFunction::DateTrunc,
2526+
Expr::Literal(ScalarValue::Utf8(Some(granularity))),
2527+
Expr::Column(column),
2528+
Some(PushToCubeContext {
2529+
ungrouped_scan_node,
2530+
known_join_subqueries,
2531+
}),
2532+
) = (&fun, &args[0], &args[1], push_to_cube_context)
2533+
{
2534+
let granularity = granularity.to_ascii_lowercase();
2535+
// Security check to prevent SQL injection
2536+
if granularity_str_to_int_order(&granularity, Some(false)).is_some()
2537+
&& subqueries.get(&column.flat_name()).is_none()
2538+
&& !column
2539+
.relation
2540+
.as_ref()
2541+
.map(|relation| known_join_subqueries.contains(relation))
2542+
.unwrap_or(false)
2543+
{
2544+
if let Ok(MemberField::Member(regular_member)) =
2545+
Self::find_member_in_ungrouped_scan(ungrouped_scan_node, column)
2546+
{
2547+
// TODO: check if member is a time dimension
2548+
if let MemberField::Member(time_dimension_member) =
2549+
MemberField::time_dimension(
2550+
regular_member.member.clone(),
2551+
granularity,
2552+
)
2553+
{
2554+
return Ok((
2555+
format!("${{{}}}", time_dimension_member.field_name),
2556+
sql_query,
2557+
));
2558+
}
2559+
}
2560+
}
2561+
}
2562+
}
25232563
if let BuiltinScalarFunction::DatePart = &fun {
25242564
if args.len() >= 2 {
25252565
match &args[0] {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14572,8 +14572,7 @@ ORDER BY "source"."str0" ASC
1457214572

1457314573
let logical_plan = query_plan.as_logical_plan();
1457414574
let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql;
14575-
assert!(sql.contains("DATETIME_TRUNC("));
14576-
assert!(sql.contains("WEEK(MONDAY)"));
14575+
assert!(sql.contains(".week"));
1457714576
}
1457814577

1457914578
#[tokio::test]

rust/cubesql/cubesql/src/compile/test/test_wrapper.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,11 +1657,11 @@ GROUP BY
16571657
let dimensions = request.dimensions.unwrap();
16581658
assert_eq!(dimensions.len(), 1);
16591659
let dimension = &dimensions[0];
1660-
assert!(dimension.contains("DATE_TRUNC"));
1660+
assert!(dimension.contains(".day"));
16611661
let segments = request.segments.unwrap();
16621662
assert_eq!(segments.len(), 1);
16631663
let segment = &segments[0];
1664-
assert!(segment.contains("DATE_TRUNC"));
1664+
assert!(segment.contains(".day"));
16651665
}
16661666

16671667
/// Aggregation with falsy filter should NOT get pushed to CubeScan with limit=0

0 commit comments

Comments
 (0)