From 54c2f01e652c43b12e8853dbc85b35c921c1c433 Mon Sep 17 00:00:00 2001 From: Tom Tankilevitch Date: Sat, 13 Sep 2025 21:15:19 +0300 Subject: [PATCH 1/5] fix(schema-compiler): use TO_TIMESTAMP_TZ for ISO 8601 with Z; keep index-friendly casts; add Oracle unit test --- .../src/adapter/OracleQuery.ts | 7 ++- .../test/unit/oracle-query.test.ts | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts diff --git a/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts b/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts index d5e4871b88497..46fdfaa0e7ed7 100644 --- a/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts @@ -71,11 +71,14 @@ export class OracleQuery extends BaseQuery { } public dateTimeCast(value) { - return `to_date(:"${value}", 'YYYY-MM-DD"T"HH24:MI:SS"Z"')`; + // Use timezone-aware parsing for ISO 8601 with milliseconds and trailing 'Z', then cast to DATE + // to preserve index-friendly comparisons against DATE columns. + return `CAST(TO_TIMESTAMP_TZ(:"${value}", 'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"') AS DATE)`; } public timeStampCast(value) { - return this.dateTimeCast(value); + // Return timezone-aware timestamp for TIMESTAMP comparisons + return `TO_TIMESTAMP_TZ(:"${value}", 'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"')`; } public timeStampParam(timeDimension) { diff --git a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts new file mode 100644 index 0000000000000..501fc2aa20fa9 --- /dev/null +++ b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts @@ -0,0 +1,56 @@ +import { OracleQuery } from '../../src/adapter/OracleQuery'; +import { prepareJsCompiler } from './PrepareCompiler'; + +describe('OracleQuery', () => { + const { compiler, joinGraph, cubeEvaluator } = prepareJsCompiler(` + cube(\`visitors\`, { + sql: \` + select * from visitors + \`, + + measures: { + count: { + type: 'count' + } + }, + + dimensions: { + id: { + sql: 'id', + type: 'number', + primaryKey: true + }, + createdAt: { + type: 'time', + sql: 'created_at' + } + } + }) + `, { adapter: 'oracle' }); + + it('uses to_date with seconds precision and preserves trailing Z', async () => { + await compiler.compile(); + + const query = new OracleQuery( + { joinGraph, cubeEvaluator, compiler }, + { + measures: ['visitors.count'], + timeDimensions: [ + { + dimension: 'visitors.createdAt', + dateRange: ['2024-02-01', '2024-02-02'], + granularity: 'day' + } + ], + timezone: 'UTC' + } + ); + + const [sql, params] = query.buildSqlAndParams(); + + expect(sql).toContain("CAST(TO_TIMESTAMP_TZ(:\"?\", 'YYYY-MM-DD\"T\"HH24:MI:SS.FF\"Z\"') AS DATE)"); + expect(params).toEqual(['2024-02-01T00:00:00.000Z']); + }); +}); + + From cc179a0478d0fc7393da52dda3a1946eb624a1c3 Mon Sep 17 00:00:00 2001 From: Tom Tankilevitch Date: Fri, 19 Sep 2025 02:45:26 +0300 Subject: [PATCH 2/5] fix lint --- .../cubejs-schema-compiler/test/unit/oracle-query.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts index 501fc2aa20fa9..b23481fae272c 100644 --- a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts @@ -48,9 +48,7 @@ describe('OracleQuery', () => { const [sql, params] = query.buildSqlAndParams(); - expect(sql).toContain("CAST(TO_TIMESTAMP_TZ(:\"?\", 'YYYY-MM-DD\"T\"HH24:MI:SS.FF\"Z\"') AS DATE)"); + expect(sql).toContain('CAST(TO_TIMESTAMP_TZ(:"?", \'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"\') AS DATE)'); expect(params).toEqual(['2024-02-01T00:00:00.000Z']); }); }); - - From f9bf9c913c8b136e1e25b7139c191dd5b4097981 Mon Sep 17 00:00:00 2001 From: Tom Tankilevitch Date: Fri, 19 Sep 2025 10:49:39 +0300 Subject: [PATCH 3/5] fix --- packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts b/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts index 46fdfaa0e7ed7..a7fcbeb90a26d 100644 --- a/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts @@ -82,7 +82,7 @@ export class OracleQuery extends BaseQuery { } public timeStampParam(timeDimension) { - return timeDimension.dateFieldType() === 'string' ? ':"?"' : this.timeStampCast('?'); + return timeDimension.dateFieldType() === 'string' ? ':"?"' : this.dateTimeCast('?'); } public timeGroupedColumn(granularity, dimension) { From 19c8d9689964d0d078535d8570fa059c42c38aee Mon Sep 17 00:00:00 2001 From: Tom Tankilevitch Date: Sun, 12 Oct 2025 19:58:01 +0700 Subject: [PATCH 4/5] fix(schema-compiler): Update Oracle test to expect both date range parameters --- packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts index b23481fae272c..327aa856d0ba4 100644 --- a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts @@ -49,6 +49,6 @@ describe('OracleQuery', () => { const [sql, params] = query.buildSqlAndParams(); expect(sql).toContain('CAST(TO_TIMESTAMP_TZ(:"?", \'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"\') AS DATE)'); - expect(params).toEqual(['2024-02-01T00:00:00.000Z']); + expect(params).toEqual(['2024-02-01T00:00:00.000Z', '2024-02-02T23:59:59.999Z']); }); }); From 3529c9b05ab30e2abc7000717b3f8e065e8373dc Mon Sep 17 00:00:00 2001 From: Tom Tankilevitch Date: Sun, 12 Oct 2025 20:15:43 +0700 Subject: [PATCH 5/5] fix --- .../cubejs-schema-compiler/test/unit/oracle-query.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts index 327aa856d0ba4..97e182f06ccd8 100644 --- a/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/oracle-query.test.ts @@ -28,7 +28,7 @@ describe('OracleQuery', () => { }) `, { adapter: 'oracle' }); - it('uses to_date with seconds precision and preserves trailing Z', async () => { + it('uses TO_TIMESTAMP_TZ with milliseconds precision and preserves trailing Z', async () => { await compiler.compile(); const query = new OracleQuery( @@ -48,7 +48,7 @@ describe('OracleQuery', () => { const [sql, params] = query.buildSqlAndParams(); - expect(sql).toContain('CAST(TO_TIMESTAMP_TZ(:"?", \'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"\') AS DATE)'); + expect(sql).toContain('TO_TIMESTAMP_TZ(:"?", \'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"\')'); expect(params).toEqual(['2024-02-01T00:00:00.000Z', '2024-02-02T23:59:59.999Z']); }); });