diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 61392c094b3a4..be70314f75855 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -958,8 +958,7 @@ export class BaseQuery { .map( d => [ d, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - (dateFrom, dateTo, dateField, dimensionDateFrom, dimensionDateTo) => `${dateField} >= ${dimensionDateFrom} AND ${dateField} <= ${dateTo}` + (_dateFrom, dateTo, dateField, dimensionDateFrom, _dimensionDateTo) => `${dateField} >= ${dimensionDateFrom} AND ${dateField} <= ${dateTo}` ] ); } @@ -970,7 +969,7 @@ export class BaseQuery { .map( d => [ d, - (dateFrom, dateTo, dateField, dimensionDateFrom, dimensionDateTo, isFromStartToEnd) => `${dateField} >= ${this.timeGroupedColumn(granularity, dateFrom)} AND ${dateField} <= ${dateTo}` + (dateFrom, dateTo, dateField, _dimensionDateFrom, _dimensionDateTo, _isFromStartToEnd) => `${dateField} >= ${this.timeGroupedColumn(granularity, dateFrom)} AND ${dateField} <= ${dateTo}` ] ); } @@ -980,7 +979,7 @@ export class BaseQuery { return this.timeDimensions .filter(td => td.granularity) .map( - d => [d, (dateFrom, dateTo, dateField, dimensionDateFrom, dimensionDateTo, isFromStartToEnd) => { + d => [d, (dateFrom, dateTo, dateField, _dimensionDateFrom, _dimensionDateTo, isFromStartToEnd) => { // dateFrom based window const conditions = []; if (trailingInterval !== 'unbounded') { @@ -1788,6 +1787,13 @@ export class BaseQuery { const dateJoinConditionSql = dateJoinCondition.map( ([d, f]) => f( + // Time-series table is generated differently in different dialects, + // but some dialects (like BigQuery) require strict date types and can not automatically convert + // between date and timestamp for comparisons, at the same time, time dimensions are expected to be + // timestamps, so we need to align types for join conditions/comparisons. + // But we can't do it here, as it would break interval maths used in some types of + // rolling window join conditions in some dialects (like Redshift), so we need to + // do casts granularly in rolling window join conditions functions. `${d.dateSeriesAliasName()}.${this.escapeColumnName('date_from')}`, `${d.dateSeriesAliasName()}.${this.escapeColumnName('date_to')}`, `${baseQueryAlias}.${d.aliasName()}`, @@ -1822,9 +1828,13 @@ export class BaseQuery { .join(', '); } + /** + * BigQuery has strict date type and can not automatically convert between date + * and timestamp, so we override dateFromStartToEndConditionSql() in BigQuery Dialect + * @protected + */ dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, isFromStartToEnd) { return dateJoinCondition.map( - // TODO these weird conversions to be strict typed for big query. // TODO Consider adding strict definitions of local and UTC time type ([d, f]) => ({ filterToWhere: () => { @@ -3906,6 +3916,7 @@ export class BaseQuery { like_escape: '{{ like_expr }} ESCAPE {{ escape_char }}', within_group: '{{ fun_sql }} WITHIN GROUP (ORDER BY {{ within_group_concat }})', concat_strings: '{{ strings | join(\' || \' ) }}', + rolling_window_expr_timestamp_cast: '{{ value }}' }, tesseract: { ilike: '{{ expr }} {% if negated %}NOT {% endif %}ILIKE {{ pattern }}', // May require different overloads in Tesseract than the ilike from expressions used in SQLAPI. diff --git a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts index c13e26d224f37..e231bbca93bbe 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts @@ -42,7 +42,7 @@ export class BigqueryQuery extends BaseQuery { } public convertTz(field) { - return `DATETIME(${this.timeStampCast(field)}, '${this.timezone}')`; + return `TIMESTAMP(DATETIME(${field}), '${this.timezone}')`; } public timeStampCast(value) { @@ -58,7 +58,7 @@ export class BigqueryQuery extends BaseQuery { } public timeGroupedColumn(granularity, dimension) { - return `DATETIME_TRUNC(${dimension}, ${GRANULARITY_TO_INTERVAL[granularity]})`; + return this.timeStampCast(`DATETIME_TRUNC(${dimension}, ${GRANULARITY_TO_INTERVAL[granularity]})`); } /** @@ -72,7 +72,7 @@ export class BigqueryQuery extends BaseQuery { return `(${this.dateTimeCast(`'${origin}'`)} + INTERVAL ${intervalFormatted} * CAST(FLOOR( - DATETIME_DIFF(${source}, ${this.dateTimeCast(`'${origin}'`)}, ${timeUnit}) / + DATETIME_DIFF(${this.dateTimeCast(source)}, ${this.dateTimeCast(`'${origin}'`)}, ${timeUnit}) / DATETIME_DIFF(${beginOfTime} + INTERVAL ${intervalFormatted}, ${beginOfTime}, ${timeUnit}) ) AS INT64))`; } @@ -182,31 +182,31 @@ export class BigqueryQuery extends BaseQuery { } public subtractInterval(date, interval) { - return `DATETIME_SUB(${date}, INTERVAL ${this.formatInterval(interval)[0]})`; - } - - public addInterval(date, interval) { - return `DATETIME_ADD(${date}, INTERVAL ${this.formatInterval(interval)[0]})`; - } - - public subtractTimestampInterval(date, interval) { const [intervalFormatted, timeUnit] = this.formatInterval(interval); - if (['YEAR', 'MONTH', 'QUARTER'].includes(timeUnit)) { + if (['YEAR', 'MONTH', 'QUARTER'].includes(timeUnit) || intervalFormatted.includes('WEEK')) { return this.timeStampCast(`DATETIME_SUB(DATETIME(${date}), INTERVAL ${intervalFormatted})`); } return `TIMESTAMP_SUB(${date}, INTERVAL ${intervalFormatted})`; } - public addTimestampInterval(date, interval) { + public addInterval(date, interval) { const [intervalFormatted, timeUnit] = this.formatInterval(interval); - if (['YEAR', 'MONTH', 'QUARTER'].includes(timeUnit)) { + if (['YEAR', 'MONTH', 'QUARTER'].includes(timeUnit) || intervalFormatted.includes('WEEK')) { return this.timeStampCast(`DATETIME_ADD(DATETIME(${date}), INTERVAL ${intervalFormatted})`); } return `TIMESTAMP_ADD(${date}, INTERVAL ${intervalFormatted})`; } + public subtractTimestampInterval(timestamp, interval) { + return this.subtractInterval(timestamp, interval); + } + + public addTimestampInterval(timestamp, interval) { + return this.addInterval(timestamp, interval); + } + public nowTimestampSql() { return 'CURRENT_TIMESTAMP()'; } @@ -215,6 +215,90 @@ export class BigqueryQuery extends BaseQuery { return `UNIX_SECONDS(${this.nowTimestampSql()})`; } + /** + * Should be protected, but BaseQuery is in js + * Overridden from BaseQuery to support BigQuery strict data types for + * joining conditions (note timeStampCast) + */ + public override runningTotalDateJoinCondition() { + return this.timeDimensions + .map( + d => [ + d, + (_dateFrom: string, dateTo: string, dateField: string, dimensionDateFrom: string, _dimensionDateTo: string) => `${dateField} >= ${dimensionDateFrom} AND ${dateField} <= ${this.timeStampCast(dateTo)}` + ] + ); + } + + /** + * Should be protected, but BaseQuery is in js + * Overridden from BaseQuery to support BigQuery strict data types for + * joining conditions (note timeStampCast) + */ + public override rollingWindowToDateJoinCondition(granularity) { + return this.timeDimensions + .filter(td => td.granularity) + .map( + d => [ + d, + (dateFrom: string, dateTo: string, dateField: string, _dimensionDateFrom: string, _dimensionDateTo: string, _isFromStartToEnd: boolean) => `${dateField} >= ${this.timeGroupedColumn(granularity, dateFrom)} AND ${dateField} <= ${this.timeStampCast(dateTo)}` + ] + ); + } + + /** + * Should be protected, but BaseQuery is in js + * Overridden from BaseQuery to support BigQuery strict data types for + * joining conditions (note timeStampCast) + */ + public override rollingWindowDateJoinCondition(trailingInterval, leadingInterval, offset) { + offset = offset || 'end'; + return this.timeDimensions + .filter(td => td.granularity) + .map( + d => [d, (dateFrom: string, dateTo: string, dateField: string, _dimensionDateFrom: string, _dimensionDateTo: string, isFromStartToEnd: boolean) => { + // dateFrom based window + const conditions: string[] = []; + if (trailingInterval !== 'unbounded') { + const startDate = isFromStartToEnd || offset === 'start' ? dateFrom : dateTo; + const trailingStart = trailingInterval ? this.subtractInterval(startDate, trailingInterval) : startDate; + const sign = offset === 'start' ? '>=' : '>'; + conditions.push(`${dateField} ${sign} ${this.timeStampCast(trailingStart)}`); + } + if (leadingInterval !== 'unbounded') { + const endDate = isFromStartToEnd || offset === 'end' ? dateTo : dateFrom; + const leadingEnd = leadingInterval ? this.addInterval(endDate, leadingInterval) : endDate; + const sign = offset === 'end' ? '<=' : '<'; + conditions.push(`${dateField} ${sign} ${this.timeStampCast(leadingEnd)}`); + } + return conditions.length ? conditions.join(' AND ') : '1 = 1'; + }] + ); + } + + // Should be protected, but BaseQuery is in js + public override dateFromStartToEndConditionSql(dateJoinCondition, fromRollup, isFromStartToEnd) { + return dateJoinCondition.map( + ([d, f]) => ({ + filterToWhere: () => { + const timeSeries = d.timeSeries(); + return f( + isFromStartToEnd ? + this.timeStampCast(this.paramAllocator.allocateParam(timeSeries[0][0])) : + `${this.timeStampInClientTz(d.dateFromParam())}`, + isFromStartToEnd ? + this.timeStampCast(this.paramAllocator.allocateParam(timeSeries[timeSeries.length - 1][1])) : + `${this.timeStampInClientTz(d.dateToParam())}`, + `${fromRollup ? this.dimensionSql(d) : d.convertedToTz()}`, + `${this.timeStampInClientTz(d.dateFromParam())}`, + `${this.timeStampInClientTz(d.dateToParam())}`, + isFromStartToEnd + ); + } + }) + ); + } + // eslint-disable-next-line no-unused-vars public preAggregationLoadSql(cube, preAggregation, tableName) { return this.preAggregationSql(cube, preAggregation); @@ -250,7 +334,7 @@ export class BigqueryQuery extends BaseQuery { const templates = super.sqlTemplates(); templates.quotes.identifiers = '`'; templates.quotes.escape = '\\`'; - templates.functions.DATETRUNC = 'DATETIME_TRUNC(CAST({{ args[1] }} AS DATETIME), {% if date_part|upper == \'WEEK\' %}{{ \'WEEK(MONDAY)\' }}{% else %}{{ date_part }}{% endif %})'; + templates.functions.DATETRUNC = 'TIMESTAMP(DATETIME_TRUNC(CAST({{ args[1] }} AS DATETIME), {% if date_part|upper == \'WEEK\' %}{{ \'WEEK(MONDAY)\' }}{% else %}{{ date_part }}{% endif %}))'; templates.functions.LOG = 'LOG({{ args_concat }}{% if args[1] is undefined %}, 10{% endif %})'; templates.functions.BTRIM = 'TRIM({{ args_concat }})'; templates.functions.STRPOS = 'STRPOS({{ args_concat }})'; @@ -263,7 +347,8 @@ export class BigqueryQuery extends BaseQuery { templates.expressions.binary = '{% if op == \'%\' %}MOD({{ left }}, {{ right }}){% else %}({{ left }} {{ op }} {{ right }}){% endif %}'; templates.expressions.interval = 'INTERVAL {{ interval }}'; templates.expressions.extract = 'EXTRACT({% if date_part == \'DOW\' %}DAYOFWEEK{% elif date_part == \'DOY\' %}DAYOFYEAR{% else %}{{ date_part }}{% endif %} FROM {{ expr }})'; - templates.expressions.timestamp_literal = 'DATETIME(TIMESTAMP(\'{{ value }}\'))'; + templates.expressions.timestamp_literal = 'TIMESTAMP(\'{{ value }}\')'; + templates.expressions.rolling_window_expr_timestamp_cast = 'TIMESTAMP({{ value }})'; delete templates.expressions.ilike; delete templates.expressions.like_escape; templates.filters.like_pattern = 'CONCAT({% if start_wild %}\'%\'{% else %}\'\'{% endif %}, LOWER({{ value }}), {% if end_wild %}\'%\'{% else %}\'\'{% endif %})'; diff --git a/packages/cubejs-testing-drivers/fixtures/mssql.json b/packages/cubejs-testing-drivers/fixtures/mssql.json index 76d595fc385be..5cad09cc45838 100644 --- a/packages/cubejs-testing-drivers/fixtures/mssql.json +++ b/packages/cubejs-testing-drivers/fixtures/mssql.json @@ -161,6 +161,8 @@ "SQL API: Extended nested Rollup over asterisk", "SQL API: ungrouped pre-agg", "SQL API: NULLS FIRST/LAST SQL push down", - "SQL API: SQL push down push to cube quoted alias" + "SQL API: SQL push down push to cube quoted alias", + "SQL API: Date/time comparison with SQL push down", + "SQL API: Date/time comparison with date_trunc with SQL push down" ] } diff --git a/packages/cubejs-testing-drivers/fixtures/mysql.json b/packages/cubejs-testing-drivers/fixtures/mysql.json index 56167c862c4e8..6631bb00918c2 100644 --- a/packages/cubejs-testing-drivers/fixtures/mysql.json +++ b/packages/cubejs-testing-drivers/fixtures/mysql.json @@ -157,6 +157,7 @@ "SQL API: Nested Rollup with aliases", "SQL API: Nested Rollup over asterisk", "SQL API: Extended nested Rollup over asterisk", - "SQL API: SQL push down push to cube quoted alias" + "SQL API: SQL push down push to cube quoted alias", + "SQL API: Date/time comparison with date_trunc with SQL push down" ] } diff --git a/packages/cubejs-testing-drivers/src/tests/testQueries.ts b/packages/cubejs-testing-drivers/src/tests/testQueries.ts index 7b90cb0680cd2..e4d52b55f04ab 100644 --- a/packages/cubejs-testing-drivers/src/tests/testQueries.ts +++ b/packages/cubejs-testing-drivers/src/tests/testQueries.ts @@ -2118,5 +2118,25 @@ from `); expect(res.rows).toMatchSnapshot(); }); + + executePg('SQL API: Date/time comparison with SQL push down', async (connection) => { + const res = await connection.query(` + SELECT MEASURE(BigECommerce.rollingCountBy2Day) + FROM BigECommerce + WHERE BigECommerce.orderDate < CAST('2021-01-01' AS TIMESTAMP) AND + LOWER("city") = 'columbus' + `); + expect(res.rows).toMatchSnapshot(); + }); + + executePg('SQL API: Date/time comparison with date_trunc with SQL push down', async (connection) => { + const res = await connection.query(` + SELECT MEASURE(BigECommerce.rollingCountBy2Week) + FROM BigECommerce + WHERE date_trunc('day', BigECommerce.orderDate) < CAST('2021-01-01' AS TIMESTAMP) AND + LOWER("city") = 'columbus' + `); + expect(res.rows).toMatchSnapshot(); + }); }); } diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/athena-export-bucket-s3-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/athena-export-bucket-s3-full.test.ts.snap index 0a5ec1590374a..c4185cbc02b0d 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/athena-export-bucket-s3-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/athena-export-bucket-s3-full.test.ts.snap @@ -8007,3 +8007,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/athena-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/athena-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-export-bucket-gcs-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-export-bucket-gcs-full.test.ts.snap index 4ede85c023a74..696d5864d11ee 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-export-bucket-gcs-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/bigquery-export-bucket-gcs-full.test.ts.snap @@ -1,5 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Queries with the @cubejs-backend/bigquery-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/bigquery-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; + exports[`Queries with the @cubejs-backend/bigquery-driver SQL API: NULLS FIRST/LAST SQL push down: nulls_first_last_sql_push_down 1`] = ` Array [ Object { diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-full.test.ts.snap index f1275b929ff23..5408ba73216ca 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-full.test.ts.snap @@ -8952,3 +8952,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/clickhouse-driver export-bucket-s3 SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver export-bucket-s3 SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-prefix-full.test.ts.snap index 343055c8a4b3a..81ae022e92d68 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-export-bucket-s3-prefix-full.test.ts.snap @@ -8952,3 +8952,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/clickhouse-driver export-bucket-s3-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver export-bucket-s3-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap index 2221fdc128706..876500d525120 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/clickhouse-full.test.ts.snap @@ -8952,3 +8952,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/clickhouse-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/clickhouse-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-full.test.ts.snap index 05ff7008ee367..5536ac59ddb91 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-full.test.ts.snap @@ -14342,3 +14342,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-azure SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-azure SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-prefix-full.test.ts.snap index 050e772bc9269..96c4f6fe6042d 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-azure-prefix-full.test.ts.snap @@ -14147,3 +14147,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-azure-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-azure-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-full.test.ts.snap index 6d89ef63971ab..ccb26104fefcc 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-full.test.ts.snap @@ -14342,3 +14342,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-gcs SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-gcs SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-prefix-full.test.ts.snap index 28fbad50da184..129ff15bd53b2 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-gcs-prefix-full.test.ts.snap @@ -14147,3 +14147,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-gcs-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-gcs-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-full.test.ts.snap index f759420e12bc3..9ee352932017c 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-full.test.ts.snap @@ -14342,3 +14342,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-s3 SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-s3 SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-prefix-full.test.ts.snap index 24c2cc871b852..663b8108366cf 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-export-bucket-s3-prefix-full.test.ts.snap @@ -14147,3 +14147,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-s3-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver export-bucket-s3-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap index a54b78fd4398d..3f9d95646da5f 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/databricks-jdbc-full.test.ts.snap @@ -14342,3 +14342,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/databricks-jdbc-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap index 052a00c0734b3..c4380197bf3bc 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/mysql-full.test.ts.snap @@ -7395,3 +7395,11 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/mysql-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap index c432883af4846..7bfe549bb279d 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/postgres-full.test.ts.snap @@ -1797,6 +1797,22 @@ Array [ ] `; +exports[`Queries with the @cubejs-backend/postgres-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/postgres-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; + exports[`Queries with the @cubejs-backend/postgres-driver SQL API: Extended nested Rollup over asterisk: extended_nested_rollup_over_asterisk 1`] = ` Array [ Object { diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/redshift-export-bucket-s3-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/redshift-export-bucket-s3-full.test.ts.snap index e463bcb330a7d..5c9df8f069749 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/redshift-export-bucket-s3-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/redshift-export-bucket-s3-full.test.ts.snap @@ -16260,3 +16260,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/redshift-driver export-bucket-s3 SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/redshift-driver export-bucket-s3 SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/redshift-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/redshift-full.test.ts.snap index feb96785b15b6..c7004a6977685 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/redshift-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/redshift-full.test.ts.snap @@ -16260,3 +16260,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/redshift-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/redshift-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-encrypted-pk-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-encrypted-pk-full.test.ts.snap index fc4da49bdd500..fe3816af0ca56 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-encrypted-pk-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-encrypted-pk-full.test.ts.snap @@ -16415,3 +16415,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver encrypted-pk SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver encrypted-pk SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-full.test.ts.snap index bd40134111d76..d48b3808d3e60 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-full.test.ts.snap @@ -16610,3 +16610,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-azure SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-azure SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-prefix-full.test.ts.snap index 31f905f778f3f..1a06bccf846c2 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-prefix-full.test.ts.snap @@ -16415,3 +16415,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-azure-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-azure-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-via-storage-integration-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-via-storage-integration-full.test.ts.snap index 876ca6d72c6e7..6d51f51c70165 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-via-storage-integration-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-azure-via-storage-integration-full.test.ts.snap @@ -16610,3 +16610,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-azure-via-storage-integration SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-azure-via-storage-integration SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-full.test.ts.snap index f271de780bbdb..9f447578479df 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-full.test.ts.snap @@ -16610,3 +16610,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-gcs SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-gcs SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-prefix-full.test.ts.snap index f19a5725b3af5..331a2f89b84cd 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-gcs-prefix-full.test.ts.snap @@ -16415,3 +16415,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-gcs-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-gcs-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-full.test.ts.snap index 218ba02038682..08cd267a3e464 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-full.test.ts.snap @@ -16610,3 +16610,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-s3 SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-s3 SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-prefix-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-prefix-full.test.ts.snap index 0d99dace33347..2100fbb39607f 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-prefix-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-export-bucket-s3-prefix-full.test.ts.snap @@ -16415,3 +16415,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-s3-prefix SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver export-bucket-s3-prefix SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap index e22c2af7604a0..91e49a4e34b03 100644 --- a/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap +++ b/packages/cubejs-testing-drivers/test/__snapshots__/snowflake-full.test.ts.snap @@ -16610,3 +16610,19 @@ Array [ }, ] `; + +exports[`Queries with the @cubejs-backend/snowflake-driver SQL API: Date/time comparison with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Day)": "12", + }, +] +`; + +exports[`Queries with the @cubejs-backend/snowflake-driver SQL API: Date/time comparison with date_trunc with SQL push down 1`] = ` +Array [ + Object { + "measure(BigECommerce.rollingCountBy2Week)": "12", + }, +] +`; diff --git a/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs b/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs index d7d7b896f191c..93c3bfa3a458c 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs @@ -57,6 +57,7 @@ impl RegularRollingWindowJoinCondition { start_date }; + let trailing_start = templates.rolling_window_expr_timestamp_cast(&trailing_start)?; let sign = if self.offset == "start" { ">=" } else { ">" }; conditions.push(format!("{date_column} {sign} {trailing_start}")); @@ -75,6 +76,7 @@ impl RegularRollingWindowJoinCondition { end_date }; + let leading_end = templates.rolling_window_expr_timestamp_cast(&leading_end)?; let sign = if self.offset == "end" { "<=" } else { "<" }; conditions.push(format!("{date_column} {sign} {leading_end}")); @@ -109,6 +111,7 @@ impl RollingTotalJoinCondition { let date_column = self.time_dimension.to_sql(templates, context)?; let date_to = templates.column_reference(&Some(self.time_series_source.clone()), "date_to")?; + let date_to = templates.rolling_window_expr_timestamp_cast(&date_to)?; let result = format!("{date_column} <= {date_to}"); Ok(result) } @@ -146,6 +149,8 @@ impl ToDateRollingWindowJoinCondition { templates.column_reference(&Some(self.time_series_source.clone()), "date_to")?; let date_to = templates.column_reference(&Some(self.time_series_source.clone()), "date_from")?; + let date_from = templates.rolling_window_expr_timestamp_cast(&date_from)?; + let date_to = templates.rolling_window_expr_timestamp_cast(&date_to)?; let grouped_from = templates.time_grouped_column(self.granularity.clone(), date_from)?; let result = format!("{date_column} >= {grouped_from} and {date_column} <= {date_to}"); Ok(result) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs index a54dae7dc1f64..e0738d93b3fc5 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/filter/base_filter.rs @@ -397,6 +397,8 @@ impl BaseFilter { ) -> Result<(String, String), CubeError> { let from_expr = format!("min({})", plan_templates.quote_identifier("date_from")?); let to_expr = format!("max({})", plan_templates.quote_identifier("date_to")?); + let from_expr = plan_templates.time_stamp_cast(from_expr)?; + let to_expr = plan_templates.time_stamp_cast(to_expr)?; let alias = format!("value"); let time_series_cte_name = format!("time_series"); // FIXME May be should be passed as parameter diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs index a5931f5f708f4..3cdb0957e33d8 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs @@ -702,4 +702,13 @@ impl PlanSqlTemplates { }, ) } + pub fn rolling_window_expr_timestamp_cast(&self, value: &str) -> Result { + self.render.render_template( + &"expressions/rolling_window_expr_timestamp_cast", + context! { + value => value + + }, + ) + } }