Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ export class DatabricksQuery extends BaseQuery {
public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.CURRENTDATE = 'CURRENT_DATE';
templates.functions.UTCTIMESTAMP = 'TO_UTC_TIMESTAMP(CURRENT_TIMESTAMP(), CURRENT_TIMEZONE())';
templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})';
templates.functions.DATEPART = 'DATE_PART({{ args_concat }})';
templates.functions.BTRIM = 'TRIM({% if args[1] is defined %}{{ args[1] }} FROM {% endif %}{{ args[0] }})';
Expand Down
3 changes: 3 additions & 0 deletions packages/cubejs-druid-driver/src/DruidQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export class DruidQuery extends BaseQuery {
delete templates.expressions.like_escape;
templates.filters.like_pattern = 'CONCAT({% if start_wild %}\'%\'{% else %}\'\'{% endif %}, LOWER({{ value }}), {% if end_wild %}\'%\'{% else %}\'\'{% endif %})';
templates.tesseract.ilike = 'LOWER({{ expr }}) {% if negated %}NOT {% endif %}LIKE {{ pattern }}';
// Druid evaluates CURRENT_TIMESTAMP in the sqlTimeZone query context, which
// defaults to UTC — assumes the connection does not override sqlTimeZone
templates.functions.UTCTIMESTAMP = 'CURRENT_TIMESTAMP';

return templates;
}
Expand Down
3 changes: 3 additions & 0 deletions packages/cubejs-duckdb-driver/src/DuckDBQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export class DuckDBQuery extends BaseQuery {
public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})';
// AT TIME ZONE on TIMESTAMPTZ yields a naive TIMESTAMP in UTC (requires the ICU
// extension, which is bundled and autoloaded in the DuckDB builds used by the driver)
templates.functions.UTCTIMESTAMP = '(NOW() AT TIME ZONE \'UTC\')';
templates.functions.LEAST = 'LEAST({{ args_concat }})';
templates.functions.GREATEST = 'GREATEST({{ args_concat }})';
templates.functions.STRING_AGG = 'STRING_AGG({% if distinct %}DISTINCT {% endif %}{{ args[0] }}, COALESCE({{ args[1] }}, \'\'))';
Expand Down
3 changes: 3 additions & 0 deletions packages/cubejs-pinot-driver/src/PinotQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ export class PinotQuery extends BaseQuery {
public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})';
// NOW() returns the current epoch millis (inherently UTC), matching the
// epoch-millis representation produced by the timestamp_literal template
templates.functions.UTCTIMESTAMP = 'NOW()';
templates.functions.STRING_AGG = 'LISTAGG({% if distinct %}DISTINCT {% endif %}{{ args_concat }})';
templates.statements.select = 'SELECT {{ select_concat | map(attribute=\'aliased\') | join(\', \') }} \n' +
'FROM (\n {{ from }}\n) AS {{ from_alias }} \n' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ export class BigqueryQuery extends BaseQuery {
// DATEADD is being rewritten to DATE_ADD
templates.functions.DATE_ADD = 'DATETIME_ADD(DATETIME({{ args[0] }}), INTERVAL {{ interval }} {{ date_part }})';
templates.functions.CURRENTDATE = 'CURRENT_DATE';
templates.functions.UTCTIMESTAMP = 'CURRENT_TIMESTAMP()';
delete templates.functions.TO_CHAR;
delete templates.functions.PERCENTILECONT;
templates.expressions.binary = '{% if op == \'%\' %}MOD({{ left }}, {{ right }}){% else %}({{ left }} {{ op }} {{ right }}){% endif %}';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export class ClickHouseQuery extends BaseQuery {
public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})';
templates.functions.UTCTIMESTAMP = 'now(\'UTC\')';
templates.functions.STRING_AGG = 'arrayStringConcat(group{% if distinct %}Uniq{% endif %}Array({{ args[0] }}), {{ args[1] }})';
// TODO: Introduce additional filter in jinja? or parseDateTimeBestEffort?
// https://github.com/ClickHouse/ClickHouse/issues/19351
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ export class MssqlQuery extends BaseQuery {
const templates = super.sqlTemplates();
templates.functions.LEAST = 'LEAST({{ args_concat }})';
templates.functions.GREATEST = 'GREATEST({{ args_concat }})';
templates.functions.UTCTIMESTAMP = 'GETUTCDATE()';
// MSSQL ROUND requires 2 arguments: ROUND(number, length)
templates.functions.ROUND = 'ROUND({{ args_concat }}{% if args | length < 2 %}, 0{% endif %})';
// NOTE: MSSQL does not support DISTINCT clause. No workaround is available
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export class MysqlQuery extends BaseQuery {
public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.STRING_AGG = 'GROUP_CONCAT({% if distinct %}DISTINCT {% endif %}{{ args[0] }} SEPARATOR {{ args[1] }})';
templates.functions.UTCTIMESTAMP = 'UTC_TIMESTAMP()';
// PERCENTILE_CONT works but requires PARTITION BY
delete templates.functions.PERCENTILECONT;
templates.quotes.identifiers = '`';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ export class OracleQuery extends BaseQuery {

public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.UTCTIMESTAMP = 'SYS_EXTRACT_UTC(SYSTIMESTAMP)';
// Oracle forbids `AS` before a table/subquery alias.
templates.expressions.query_aliased = '{{ query }} {{ quoted_alias }}';
// Oracle does not support positional GROUP BY — group by expressions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export class PostgresQuery extends BaseQuery {
templates.functions.LEAST = 'LEAST({{ args_concat }})';
templates.functions.GREATEST = 'GREATEST({{ args_concat }})';
templates.functions.NOW = 'NOW({{ args_concat }})';
templates.functions.UTCTIMESTAMP = '(NOW() AT TIME ZONE \'UTC\')';
// DATEADD is being rewritten to DATE_ADD
// templates.functions.DATEADD = '({{ args[2] }} + \'{{ interval }} {{ date_part }}\'::interval)';
// TODO: is DATEDIFF expr worth documenting?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class PrestodbQuery extends BaseQuery {
templates.functions.DATEPART = 'DATE_PART({{ args_concat }})';
templates.functions.DATEDIFF = 'DATE_DIFF(\'{{ date_part }}\', {{ args[1] }}, {{ args[2] }})';
templates.functions.CURRENTDATE = 'CURRENT_DATE';
templates.functions.UTCTIMESTAMP = 'CAST(NOW() AT TIME ZONE \'UTC\' AS TIMESTAMP)';
templates.functions.TRUNC = 'TRUNCATE({{ args_concat }})';
templates.functions.STRING_AGG = 'ARRAY_JOIN(ARRAY_AGG({% if distinct %}DISTINCT {% endif %}{{ args[0] }}), COALESCE({{ args[1] }}, \'\'))';
delete templates.functions.PERCENTILECONT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ export class RedshiftQuery extends PostgresQuery {
public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.DLOG10 = 'LOG(10, {{ args_concat }})';
// Redshift clusters always run in UTC and GETDATE() is supported on compute
// nodes, unlike NOW(), which is a leader node–only function.
templates.functions.UTCTIMESTAMP = 'GETDATE()';
templates.functions.DATEDIFF = 'DATEDIFF({{ date_part }}, {{ args[1] }}, {{ args[2] }})';
templates.functions.STRING_AGG = 'LISTAGG({% if distinct %}DISTINCT {% endif %}{{ args_concat }})';
templates.statements.time_series_select = 'SELECT dates.f::timestamp date_from, dates.t::timestamp date_to \n' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class SnowflakeQuery extends BaseQuery {
templates.functions.DATEPART = 'DATE_PART({{ args_concat }})';
templates.functions.CURRENTDATE = 'CURRENT_DATE';
templates.functions.NOW = 'CURRENT_TIMESTAMP';
templates.functions.UTCTIMESTAMP = 'SYSDATE()';
templates.functions.LOG = 'LOG({% if args[1] is undefined %}10, {% endif %}{{ args_concat }})';
templates.functions.DLOG10 = 'LOG(10, {{ args_concat }})';
templates.functions.CHARACTERLENGTH = 'LENGTH({{ args[0] }})';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,14 @@ Array [
]
`;

exports[`SQL API Postgres (Data) current_timestamp subquery filter push down: current_timestamp_push_down 1`] = `
Array [
Object {
"cn": "5",
},
]
`;

exports[`SQL API Postgres (Data) date/string measures in view: date case 1`] = `
Array [
Object {
Expand Down
12 changes: 12 additions & 0 deletions packages/cubejs-testing/test/smoke-cubesql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,18 @@ describe('SQL API', () => {
expect(res.rows).toMatchSnapshot('powerbi_min_max_push_down');
});

test('current_timestamp subquery filter push down', async () => {
// CURRENT_TIMESTAMP-based bounds wrapped in scalar subqueries force
// SQL push down and require the functions/UTCTIMESTAMP template.
// All fixture rows are in the past, so the result is stable over time.
const res = await connection.query(`
SELECT COUNT(*) as cn
FROM "public"."Orders" "orders"
WHERE ("orders"."createdAt" < ((SELECT DATE_TRUNC('year', DATE_TRUNC('day', CURRENT_TIMESTAMP)))))
`);
expect(res.rows).toMatchSnapshot('current_timestamp_push_down');
});

test('no limit for non matching count push down', async () => {
const res = await connection.query(`
select
Expand Down
1 change: 1 addition & 0 deletions rust/cubesql/cubesql/src/compile/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ pub fn sql_generator(
("functions/DATEDIFF".to_string(), "DATEDIFF({{ date_part }}, {{ args[1] }}, {{ args[2] }})".to_string()),
("functions/CURRENTDATE".to_string(), "CURRENT_DATE({{ args_concat }})".to_string()),
("functions/NOW".to_string(), "NOW({{ args_concat }})".to_string()),
("functions/UTCTIMESTAMP".to_string(), "(NOW() AT TIME ZONE 'UTC')".to_string()),
("functions/DATE_ADD".to_string(), "DATE_ADD({{ args_concat }})".to_string()),
("functions/CONCAT".to_string(), "CONCAT({{ args_concat }})".to_string()),
("functions/DATE".to_string(), "DATE({{ args_concat }})".to_string()),
Expand Down
26 changes: 26 additions & 0 deletions rust/cubesql/cubesql/src/compile/test/test_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,32 @@ async fn test_simple_subquery_wrapper_filter_empty_source() {
//println!("phys plan {:?}", physical_plan);
}

#[tokio::test]
async fn test_wrapper_filter_subquery_current_timestamp() {
if !Rewriter::sql_push_down_enabled() {
return;
}
init_testing_logger();

let query_plan = convert_select_to_query_plan(
r#"
SELECT customer_gender, AVG(avgPrice) mp
FROM KibanaSampleDataEcommerce a
WHERE order_date >= (SELECT DATE_TRUNC('year', DATE_TRUNC('day', CURRENT_TIMESTAMP)))
GROUP BY 1
"#
.to_string(),
DatabaseProtocol::PostgreSQL,
)
.await;

let logical_plan = query_plan.as_logical_plan();
let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql;
assert!(sql.contains("NOW() AT TIME ZONE 'UTC'"));

let _physical_plan = query_plan.as_physical_plan().await.unwrap();
}

#[tokio::test]
async fn test_simple_subquery_wrapper_projection_aggregate_empty_source() {
if !Rewriter::sql_push_down_enabled() {
Expand Down
Loading