11const { BaseFilter, BaseQuery } = require ( '@cubejs-backend/schema-compiler' ) ;
2+ const { parseSqlInterval } = require ( '@cubejs-backend/shared' ) ;
23
34const 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
97172module . exports = DremioQuery ;
0 commit comments