Skip to content

Commit b7018c7

Browse files
fix(schema-compiler): Oracle use TO_TIMESTAMP_TZ for ISO 8601 with Z for date and timestamp (#9970)
* fix(schema-compiler): use TO_TIMESTAMP_TZ for ISO 8601 with Z; keep index-friendly casts; add Oracle unit test * fix lint * fix * fix(schema-compiler): Update Oracle test to expect both date range parameters * fix * cr comments fixes --------- Co-authored-by: Konstantin Burkalev <[email protected]>
1 parent bc16a5f commit b7018c7

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,14 @@ export class OracleQuery extends BaseQuery {
7171
}
7272

7373
public dateTimeCast(value) {
74-
return `to_date(:"${value}", 'YYYY-MM-DD"T"HH24:MI:SS"Z"')`;
74+
// Use timezone-aware parsing for ISO 8601 with milliseconds and trailing 'Z', then cast to DATE
75+
// to preserve index-friendly comparisons against DATE columns.
76+
return `CAST(TO_TIMESTAMP_TZ(:"${value}", 'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"') AS DATE)`;
7577
}
7678

7779
public timeStampCast(value) {
78-
return this.dateTimeCast(value);
80+
// Return timezone-aware timestamp for TIMESTAMP comparisons
81+
return `TO_TIMESTAMP_TZ(:"${value}", 'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"')`;
7982
}
8083

8184
public timeStampParam(timeDimension) {
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { OracleQuery } from '../../src/adapter/OracleQuery';
2+
import { prepareJsCompiler } from './PrepareCompiler';
3+
4+
describe('OracleQuery', () => {
5+
const { compiler, joinGraph, cubeEvaluator } = prepareJsCompiler(`
6+
cube(\`visitors\`, {
7+
sql: \`
8+
select * from visitors
9+
\`,
10+
11+
measures: {
12+
count: {
13+
type: 'count'
14+
}
15+
},
16+
17+
dimensions: {
18+
id: {
19+
sql: 'id',
20+
type: 'number',
21+
primaryKey: true
22+
},
23+
createdAt: {
24+
type: 'time',
25+
sql: 'created_at'
26+
}
27+
}
28+
})
29+
`, { adapter: 'oracle' });
30+
31+
it('generates TO_TIMESTAMP_TZ with millisecond precision for date range filters', async () => {
32+
await compiler.compile();
33+
34+
const query = new OracleQuery(
35+
{ joinGraph, cubeEvaluator, compiler },
36+
{
37+
measures: ['visitors.count'],
38+
timeDimensions: [
39+
{
40+
dimension: 'visitors.createdAt',
41+
dateRange: ['2024-02-01', '2024-02-02'],
42+
granularity: 'day'
43+
}
44+
],
45+
timezone: 'UTC'
46+
}
47+
);
48+
49+
const [sql, params] = query.buildSqlAndParams();
50+
51+
// Verify TO_TIMESTAMP_TZ is used with proper ISO 8601 format including milliseconds
52+
expect(sql).toContain('TO_TIMESTAMP_TZ(:"?", \'YYYY-MM-DD"T"HH24:MI:SS.FF"Z"\')');
53+
expect(sql).toMatch(/created_at\s+>=\s+TO_TIMESTAMP_TZ/);
54+
expect(sql).toMatch(/created_at\s+<=\s+TO_TIMESTAMP_TZ/);
55+
56+
// Verify parameters include millisecond precision
57+
expect(params).toEqual(['2024-02-01T00:00:00.000Z', '2024-02-02T23:59:59.999Z']);
58+
});
59+
60+
it('generates TRUNC function for day granularity grouping', async () => {
61+
await compiler.compile();
62+
63+
const query = new OracleQuery(
64+
{ joinGraph, cubeEvaluator, compiler },
65+
{
66+
measures: ['visitors.count'],
67+
timeDimensions: [
68+
{
69+
dimension: 'visitors.createdAt',
70+
dateRange: ['2024-01-01', '2024-01-31'],
71+
granularity: 'day'
72+
}
73+
],
74+
timezone: 'UTC'
75+
}
76+
);
77+
78+
const [sql, params] = query.buildSqlAndParams();
79+
80+
// Verify TRUNC with DD format for day grouping
81+
expect(sql).toContain('TRUNC("visitors".created_at, \'DD\')');
82+
expect(sql).toMatch(/GROUP BY\s+TRUNC/);
83+
expect(params).toEqual(['2024-01-01T00:00:00.000Z', '2024-01-31T23:59:59.999Z']);
84+
});
85+
86+
it('generates TRUNC function for month granularity grouping', async () => {
87+
await compiler.compile();
88+
89+
const query = new OracleQuery(
90+
{ joinGraph, cubeEvaluator, compiler },
91+
{
92+
measures: ['visitors.count'],
93+
timeDimensions: [
94+
{
95+
dimension: 'visitors.createdAt',
96+
dateRange: ['2024-01-01', '2024-12-31'],
97+
granularity: 'month'
98+
}
99+
],
100+
timezone: 'UTC'
101+
}
102+
);
103+
104+
const [sql, params] = query.buildSqlAndParams();
105+
106+
// Verify TRUNC with MM format for month grouping
107+
expect(sql).toContain('TRUNC("visitors".created_at, \'MM\')');
108+
expect(sql).toMatch(/GROUP BY\s+TRUNC/);
109+
expect(params).toEqual(['2024-01-01T00:00:00.000Z', '2024-12-31T23:59:59.999Z']);
110+
});
111+
});

0 commit comments

Comments
 (0)