Skip to content

Commit

Permalink
feat(cubesql): Support date_trunc over column filter with <=
Browse files Browse the repository at this point in the history
  • Loading branch information
MazterQyou committed Sep 29, 2022
1 parent 3f3a61c commit b30d239
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 15 deletions.
104 changes: 104 additions & 0 deletions rust/cubesql/cubesql/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11876,4 +11876,108 @@ ORDER BY \"COUNT(count)\" DESC"
}
)
}

#[tokio::test]
async fn test_quicksight_date_trunc_column_less_or_eq() {
init_logger();

let logical_plan = convert_select_to_query_plan(
r#"
SELECT date_trunc('day', "order_date") AS "uuid.order_date_tg", COUNT(*) AS "count"
FROM "public"."KibanaSampleDataEcommerce"
WHERE
"order_date" >= date_trunc('day', TO_TIMESTAMP('2020-01-01 00:00:00', 'yyyy-MM-dd HH24:mi:ss')) AND
date_trunc('day', "order_date") <= date_trunc('day', LOCALTIMESTAMP + -5 * interval '1 DAY')
GROUP BY date_trunc('day', "order_date")
ORDER BY date_trunc('day', "order_date") DESC NULLS LAST
LIMIT 2500;
"#
.to_string(),
DatabaseProtocol::PostgreSQL,
)
.await
.as_logical_plan();

let end_date = chrono::Utc::now().date().naive_utc() - chrono::Duration::days(5);
assert_eq!(
logical_plan.find_cube_scan().request,
V1LoadRequestQuery {
measures: Some(vec!["KibanaSampleDataEcommerce.count".to_string(),]),
dimensions: Some(vec![]),
segments: Some(vec![]),
time_dimensions: Some(vec![V1LoadRequestQueryTimeDimension {
dimension: "KibanaSampleDataEcommerce.order_date".to_string(),
granularity: Some("day".to_string()),
date_range: Some(json!(vec![
"2020-01-01T00:00:00.000Z".to_string(),
format!("{}T23:59:59.999Z", end_date),
]))
}]),
order: Some(vec![vec![
"KibanaSampleDataEcommerce.order_date".to_string(),
"desc".to_string()
]]),
limit: Some(2500),
offset: None,
filters: None,
}
)
}

#[tokio::test]
async fn test_quicksight_excluding_n_weeks() {
init_logger();

let logical_plan = convert_select_to_query_plan(
r#"
SELECT date_trunc('day', "order_date") AS "uuid.order_date_tg", COUNT(*) AS "count"
FROM "public"."KibanaSampleDataEcommerce"
WHERE
"order_date" >= date_trunc('day', TO_TIMESTAMP('2020-01-01 00:00:00', 'yyyy-MM-dd HH24:mi:ss')) AND
DATE_TRUNC(
'week',
"order_date" + INTERVAL '1 day'
) - INTERVAL '1 day' <= DATE_TRUNC(
'week',
LOCALTIMESTAMP + 7 * -5 * interval '1 DAY' + INTERVAL '1 day'
) - INTERVAL '1 day'
GROUP BY date_trunc('day', "order_date")
ORDER BY date_trunc('day', "order_date") DESC NULLS LAST
LIMIT 2500;
"#
.to_string(),
DatabaseProtocol::PostgreSQL,
)
.await
.as_logical_plan();

let now = chrono::Utc::now();
let duration_sub_weeks = chrono::Duration::weeks(4);
let duration_sub_days =
chrono::Duration::days(now.weekday().num_days_from_sunday() as i64 + 1);
let end_date = now.date().naive_utc() - duration_sub_weeks - duration_sub_days;
assert_eq!(
logical_plan.find_cube_scan().request,
V1LoadRequestQuery {
measures: Some(vec!["KibanaSampleDataEcommerce.count".to_string(),]),
dimensions: Some(vec![]),
segments: Some(vec![]),
time_dimensions: Some(vec![V1LoadRequestQueryTimeDimension {
dimension: "KibanaSampleDataEcommerce.order_date".to_string(),
granularity: Some("day".to_string()),
date_range: Some(json!(vec![
"2020-01-01T00:00:00.000Z".to_string(),
format!("{}T23:59:59.999Z", end_date),
]))
}]),
order: Some(vec![vec![
"KibanaSampleDataEcommerce.order_date".to_string(),
"desc".to_string()
]]),
limit: Some(2500),
offset: None,
filters: None,
}
)
}
}
107 changes: 92 additions & 15 deletions rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ impl RewriteRules for FilterRules {
"?alias_to_cube",
"?members",
),
self.transform_date_trunc_equals("?granularity", "?interval"),
self.transform_granularity_to_interval("?granularity", "?interval"),
),
// TODO define zero
rewrite(
Expand Down Expand Up @@ -911,6 +911,95 @@ impl RewriteRules for FilterRules {
"?values",
),
),
transforming_rewrite(
"filter-date-trunc-leeq",
filter_replacer(
binary_expr(
fun_expr(
"DateTrunc",
vec![literal_expr("?granularity"), column_expr("?column")],
),
"<=",
fun_expr(
"DateTrunc",
vec![literal_expr("?granularity"), literal_expr("?expr")],
),
),
"?alias_to_cube",
"?members",
),
filter_replacer(
binary_expr(
column_expr("?column"),
"<",
binary_expr(
fun_expr(
"DateTrunc",
vec![literal_expr("?granularity"), literal_expr("?expr")],
),
"+",
literal_expr("?interval"),
),
),
"?alias_to_cube",
"?members",
),
self.transform_granularity_to_interval("?granularity", "?interval"),
),
transforming_rewrite(
"filter-date-trunc-sub-leeq",
filter_replacer(
binary_expr(
binary_expr(
fun_expr(
"DateTrunc",
vec![
literal_expr("?granularity"),
binary_expr(
column_expr("?column"),
"+",
literal_expr("?same_interval"),
),
],
),
"-",
literal_expr("?same_interval"),
),
"<=",
binary_expr(
fun_expr(
"DateTrunc",
vec![literal_expr("?granularity"), literal_expr("?expr")],
),
"-",
literal_expr("?same_interval"),
),
),
"?alias_to_cube",
"?members",
),
filter_replacer(
binary_expr(
column_expr("?column"),
"<",
binary_expr(
binary_expr(
fun_expr(
"DateTrunc",
vec![literal_expr("?granularity"), literal_expr("?expr")],
),
"-",
literal_expr("?same_interval"),
),
"+",
literal_expr("?interval"),
),
),
"?alias_to_cube",
"?members",
),
self.transform_granularity_to_interval("?granularity", "?interval"),
),
rewrite(
"between-move-interval-beyond-equal-sign",
between_expr(
Expand Down Expand Up @@ -2237,7 +2326,7 @@ impl FilterRules {
}
}

fn transform_date_trunc_equals(
fn transform_granularity_to_interval(
&self,
granularity_var: &'static str,
interval_var: &'static str,
Expand All @@ -2246,19 +2335,7 @@ impl FilterRules {
let interval_var = var!(interval_var);
move |egraph, subst| {
for granularity in var_iter!(egraph[subst[granularity_var]], LiteralExprValue) {
if let ScalarValue::Utf8(Some(granularity)) = granularity {
let interval = match granularity.as_str() {
"second" => ScalarValue::IntervalDayTime(Some(1000)),
"minute" => ScalarValue::IntervalDayTime(Some(60000)),
"hour" => ScalarValue::IntervalDayTime(Some(3600000)),
"day" => ScalarValue::IntervalDayTime(Some(4294967296)),
"week" => ScalarValue::IntervalDayTime(Some(30064771072)),
"month" => ScalarValue::IntervalYearMonth(Some(1)),
"quarter" => ScalarValue::IntervalYearMonth(Some(3)),
"year" => ScalarValue::IntervalYearMonth(Some(12)),
_ => continue,
};

if let Some(interval) = utils::granularity_to_interval(granularity) {
subst.insert(
interval_var,
egraph.add(LogicalPlanLanguage::LiteralExprValue(LiteralExprValue(
Expand Down
20 changes: 20 additions & 0 deletions rust/cubesql/cubesql/src/compile/rewrite/rules/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,23 @@ pub fn parse_granularity(granularity: &ScalarValue, to_normalize: bool) -> Optio
_ => None,
}
}

pub fn granularity_to_interval(granularity: &ScalarValue) -> Option<ScalarValue> {
if let Some(granularity) = parse_granularity(granularity, false) {
let interval = match granularity.as_str() {
"second" => ScalarValue::IntervalDayTime(Some(1000)),
"minute" => ScalarValue::IntervalDayTime(Some(60000)),
"hour" => ScalarValue::IntervalDayTime(Some(3600000)),
"day" => ScalarValue::IntervalDayTime(Some(4294967296)),
"week" => ScalarValue::IntervalDayTime(Some(30064771072)),
"month" => ScalarValue::IntervalYearMonth(Some(1)),
"quarter" => ScalarValue::IntervalYearMonth(Some(3)),
"year" => ScalarValue::IntervalYearMonth(Some(12)),
_ => return None,
};

return Some(interval);
}

None
}

0 comments on commit b30d239

Please sign in to comment.