Skip to content

Commit 7cf2287

Browse files
KSDaemonwardsi
andauthored
fix(dremio-driver): Correct dremio date interval functions (#9193)
* Correct dremio date interval functions * Dremio date interval functions - support for year, month, qtr and day only * Removing space and block row padding - lint failing * Adding support for hrs, mins, seconds. Additionally, timestamp formatting corrections * fix package deps versions --------- Co-authored-by: wardsi <[email protected]>
1 parent 882a443 commit 7cf2287

File tree

2 files changed

+81
-6
lines changed

2 files changed

+81
-6
lines changed

packages/cubejs-dremio-driver/driver/DremioQuery.js

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const { BaseFilter, BaseQuery } = require('@cubejs-backend/schema-compiler');
2+
const { parseSqlInterval } = require('@cubejs-backend/shared');
23

34
const GRANULARITY_TO_INTERVAL = {
45
week: (date) => `DATE_TRUNC('week', ${date})`,
@@ -55,15 +56,39 @@ class DremioQuery extends BaseQuery {
5556
}
5657

5758
dateTimeCast(value) {
58-
return `TO_TIMESTAMP(${value})`;
59+
return `TO_TIMESTAMP(${value}, 'YYYY-MM-DD"T"HH24:MI:SS.FFF')`;
5960
}
6061

6162
subtractInterval(date, interval) {
62-
return `DATE_SUB(${date}, INTERVAL ${interval})`;
63+
const formattedTimeIntervals = this.formatInterval(interval);
64+
const intervalFormatted = formattedTimeIntervals[0];
65+
const timeUnit = formattedTimeIntervals[1];
66+
return `DATE_SUB(${date}, CAST(${intervalFormatted} as INTERVAL ${timeUnit}))`;
6367
}
6468

6569
addInterval(date, interval) {
66-
return `DATE_ADD(${date}, INTERVAL ${interval})`;
70+
const formattedTimeIntervals = this.formatInterval(interval);
71+
const intervalFormatted = formattedTimeIntervals[0];
72+
const timeUnit = formattedTimeIntervals[1];
73+
return `DATE_ADD(${date}, CAST(${intervalFormatted} as INTERVAL ${timeUnit}))`;
74+
}
75+
76+
/**
77+
* @param {string} timestamp
78+
* @param {string} interval
79+
* @returns {string}
80+
*/
81+
addTimestampInterval(timestamp, interval) {
82+
return this.addInterval(timestamp, interval);
83+
}
84+
85+
/**
86+
* @param {string} timestamp
87+
* @param {string} interval
88+
* @returns {string}
89+
*/
90+
subtractTimestampInterval(timestamp, interval) {
91+
return this.subtractInterval(timestamp, interval);
6792
}
6893

6994
timeGroupedColumn(granularity, dimension) {
@@ -78,7 +103,8 @@ class DremioQuery extends BaseQuery {
78103
const values = timeDimension.timeSeries().map(
79104
([from, to]) => `select '${from}' f, '${to}' t`
80105
).join(' UNION ALL ');
81-
return `SELECT TO_TIMESTAMP(dates.f, 'YYYY-MM-DDTHH:MI:SS.FFF') date_from, TO_TIMESTAMP(dates.t, 'YYYY-MM-DDTHH:MI:SS.FFF') date_to FROM (${values}) AS dates`;
106+
107+
return `SELECT TO_TIMESTAMP(dates.f, 'YYYY-MM-DD"T"HH24:MI:SS.FFF') date_from, TO_TIMESTAMP(dates.t, 'YYYY-MM-DD"T"HH24:MI:SS.FFF') date_to FROM (${values}) AS dates`;
82108
}
83109

84110
concatStringsSql(strings) {
@@ -92,6 +118,55 @@ class DremioQuery extends BaseQuery {
92118
wrapSegmentForDimensionSelect(sql) {
93119
return `IF(${sql}, 1, 0)`;
94120
}
121+
122+
/**
123+
* The input interval with (possible) plural units, like "1 hour 2 minutes", "2 year", "3 months", "4 weeks", "5 days", "3 months 24 days 15 minutes", ...
124+
* will be converted to Dremio dialect.
125+
* @see https://docs.dremio.com/24.3.x/reference/sql/sql-functions/functions/DATE_ADD/
126+
* @see https://docs.dremio.com/24.3.x/reference/sql/sql-functions/functions/DATE_SUB/
127+
* It returns a tuple of (formatted interval, timeUnit to use in date functions)
128+
* This function only supports the following scenarios for now:
129+
* ie. n year[s] or n quarter[s] or n month[s] or n week[s] or n day[s]
130+
*/
131+
formatInterval(interval) {
132+
const intervalParsed = parseSqlInterval(interval);
133+
const intKeys = Object.keys(intervalParsed).length;
134+
135+
if (intervalParsed.year && intKeys === 1) {
136+
return [`${intervalParsed.year}`, 'YEAR'];
137+
} else if (intervalParsed.quarter && intKeys === 1) {
138+
// dremio interval does not support quarter. Convert to month
139+
return [`${intervalParsed.quarter * 3}`, 'MONTH'];
140+
} else if (intervalParsed.week && intKeys === 1) {
141+
// dremio interval does not support week. Convert to days
142+
return [`${intervalParsed.week * 7}`, 'DAY'];
143+
} else if (intervalParsed.month && intKeys === 1) {
144+
return [`${intervalParsed.month}`, 'MONTH'];
145+
} else if (intervalParsed.month && intKeys === 1) {
146+
return [`${intervalParsed.day}`, 'DAY'];
147+
} else if (intervalParsed.hour && intKeys === 1) {
148+
return [`${intervalParsed.hour}`, 'HOUR'];
149+
} else if (intervalParsed.minute && intKeys === 1) {
150+
return [`${intervalParsed.minute}`, 'MINUTE'];
151+
} else if (intervalParsed.second && intKeys === 1) {
152+
return [`${intervalParsed.second}`, 'SECOND'];
153+
}
154+
155+
throw new Error(`Cannot transform interval expression "${interval}" to Dremio dialect`);
156+
}
157+
158+
sqlTemplates() {
159+
const templates = super.sqlTemplates();
160+
templates.functions.CURRENTDATE = 'CURRENT_DATE';
161+
templates.functions.DATETRUNC = 'DATE_TRUNC(\'{{ date_part }}\', {{ args_concat }})';
162+
templates.functions.DATEPART = 'DATE_PART(\'{{ date_part }}\', {{ args_concat }})';
163+
// really need the date locale formatting here...
164+
templates.functions.DATE = 'TO_DATE({{ args_concat }},\'YYYY-MM-DD\', 1)';
165+
templates.functions.DATEDIFF = 'DATE_DIFF(DATE, DATE_TRUNC(\'{{ date_part }}\', {{ args[1] }}), DATE_TRUNC(\'{{ date_part }}\', {{ args[2] }}))';
166+
templates.expressions.interval_single_date_part = 'CAST({{ num }} as INTERVAL {{ date_part }})';
167+
templates.quotes.identifiers = '"';
168+
return templates;
169+
}
95170
}
96171

97172
module.exports = DremioQuery;

rust/cubestore/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"author": "Cube Dev, Inc.",
2828
"license": "Apache-2.0",
2929
"devDependencies": {
30-
"@cubejs-backend/linter": "1.2.0",
30+
"@cubejs-backend/linter": "1.2.1",
3131
"@types/jest": "^27",
3232
"@types/node": "^12",
3333
"jest": "^27",
@@ -37,7 +37,7 @@
3737
"access": "public"
3838
},
3939
"dependencies": {
40-
"@cubejs-backend/shared": "1.2.0",
40+
"@cubejs-backend/shared": "1.2.1",
4141
"@octokit/core": "^3.2.5",
4242
"source-map-support": "^0.5.19"
4343
},

0 commit comments

Comments
 (0)