11import moment from 'moment-timezone' ;
2+ import { parseSqlInterval } from '@cubejs-backend/shared' ;
23import { BaseQuery } from './BaseQuery' ;
34import { BaseFilter } from './BaseFilter' ;
45import { BaseMeasure } from './BaseMeasure' ;
@@ -42,7 +43,7 @@ export class CubeStoreQuery extends BaseQuery {
4243 }
4344
4445 public timeStampCast ( value ) {
45- return `CAST(${ value } as TIMESTAMP)` ; // TODO
46+ return `CAST(${ value } as TIMESTAMP)` ;
4647 }
4748
4849 public timestampFormat ( ) {
@@ -53,18 +54,77 @@ export class CubeStoreQuery extends BaseQuery {
5354 return `to_timestamp(${ value } )` ;
5455 }
5556
56- public subtractInterval ( date , interval ) {
57- return `DATE_SUB(${ date } , INTERVAL ' ${ interval } ' )` ;
57+ public subtractInterval ( date : string , interval : string ) {
58+ return `DATE_SUB(${ date } , INTERVAL ${ this . formatInterval ( interval ) } )` ;
5859 }
5960
60- public addInterval ( date , interval ) {
61- return `DATE_ADD(${ date } , INTERVAL ' ${ interval } ' )` ;
61+ public addInterval ( date : string , interval : string ) {
62+ return `DATE_ADD(${ date } , INTERVAL ${ this . formatInterval ( interval ) } )` ;
6263 }
6364
64- public timeGroupedColumn ( granularity , dimension ) {
65+ public timeGroupedColumn ( granularity : string , dimension : string ) {
6566 return `date_trunc('${ GRANULARITY_TO_INTERVAL [ granularity ] } ', ${ dimension } )` ;
6667 }
6768
69+ /**
70+ * Returns sql for source expression floored to timestamps aligned with
71+ * intervals relative to origin timestamp point.
72+ */
73+ public dateBin ( interval : string , source : string , origin : string ) : string {
74+ return `DATE_BIN(INTERVAL ${ this . formatInterval ( interval ) } , ${ this . timeStampCast ( source ) } , ${ this . timeStampCast ( `'${ origin } '` ) } )` ;
75+ }
76+
77+ /**
78+ * The input interval with (possible) plural units, like "2 years", "3 months", "4 weeks", "5 days"...
79+ * will be converted to CubeStore (DataFusion) dialect.
80+ */
81+ private formatInterval ( interval : string ) : string {
82+ const intervalParsed = parseSqlInterval ( interval ) ;
83+ const intKeys = Object . keys ( intervalParsed ) . length ;
84+
85+ if ( intervalParsed . year && intKeys === 1 ) {
86+ return `'${ intervalParsed . year } YEAR'` ;
87+ } else if ( intervalParsed . year && intervalParsed . month && intKeys === 2 ) {
88+ return `'${ intervalParsed . year } YEAR ${ intervalParsed . month } MONTH'` ;
89+ } else if ( intervalParsed . year && intervalParsed . month && intervalParsed . quarter && intKeys === 3 ) {
90+ return `'${ intervalParsed . year } YEAR ${ intervalParsed . quarter } QUARTER ${ intervalParsed . month } MONTH'` ;
91+ } else if ( intervalParsed . quarter && intKeys === 1 ) {
92+ return `'${ intervalParsed . quarter } QUARTER'` ;
93+ } else if ( intervalParsed . quarter && intervalParsed . month && intKeys === 2 ) {
94+ return `'${ intervalParsed . quarter } QUARTER ${ intervalParsed . month } MONTH'` ;
95+ } else if ( intervalParsed . month && intKeys === 1 ) {
96+ return `'${ intervalParsed . month } MONTH'` ;
97+ } else if ( intervalParsed . week && intKeys === 1 ) {
98+ return `'${ intervalParsed . week } WEEK'` ;
99+ } else if ( intervalParsed . week && intervalParsed . day && intKeys === 2 ) {
100+ return `'${ intervalParsed . week } WEEK ${ intervalParsed . day } DAY'` ;
101+ } else if ( intervalParsed . week && intervalParsed . day && intervalParsed . hour && intKeys === 3 ) {
102+ return `'${ intervalParsed . week } WEEK ${ intervalParsed . day } DAY ${ intervalParsed . hour } HOUR'` ;
103+ } else if ( intervalParsed . week && intervalParsed . day && intervalParsed . hour && intervalParsed . minute && intKeys === 4 ) {
104+ return `'${ intervalParsed . week } WEEK ${ intervalParsed . day } DAY ${ intervalParsed . hour } HOUR ${ intervalParsed . minute } MINUTE'` ;
105+ } else if ( intervalParsed . week && intervalParsed . day && intervalParsed . hour && intervalParsed . minute && intervalParsed . second && intKeys === 5 ) {
106+ return `'${ intervalParsed . week } WEEK ${ intervalParsed . day } DAY ${ intervalParsed . hour } HOUR ${ intervalParsed . minute } MINUTE ${ intervalParsed . second } SECOND'` ;
107+ } else if ( intervalParsed . day && intKeys === 1 ) {
108+ return `'${ intervalParsed . day } DAY'` ;
109+ } else if ( intervalParsed . day && intervalParsed . hour && intKeys === 2 ) {
110+ return `'${ intervalParsed . day } DAY ${ intervalParsed . hour } HOUR'` ;
111+ } else if ( intervalParsed . day && intervalParsed . hour && intervalParsed . minute && intKeys === 3 ) {
112+ return `'${ intervalParsed . day } DAY ${ intervalParsed . hour } HOUR ${ intervalParsed . minute } MINUTE'` ;
113+ } else if ( intervalParsed . day && intervalParsed . hour && intervalParsed . minute && intervalParsed . second && intKeys === 4 ) {
114+ return `'${ intervalParsed . day } DAY ${ intervalParsed . hour } HOUR ${ intervalParsed . minute } MINUTE ${ intervalParsed . second } SECOND'` ;
115+ } else if ( intervalParsed . hour && intervalParsed . minute && intKeys === 2 ) {
116+ return `'${ intervalParsed . hour } HOUR ${ intervalParsed . minute } MINUTE'` ;
117+ } else if ( intervalParsed . hour && intervalParsed . minute && intervalParsed . second && intKeys === 3 ) {
118+ return `'${ intervalParsed . hour } HOUR ${ intervalParsed . minute } MINUTE ${ intervalParsed . second } SECOND'` ;
119+ } else if ( intervalParsed . minute && intervalParsed . second && intKeys === 2 ) {
120+ return `'${ intervalParsed . minute } MINUTE ${ intervalParsed . second } SECOND'` ;
121+ }
122+
123+ // No need to support microseconds.
124+
125+ throw new Error ( `Cannot transform interval expression "${ interval } " to CubeStore dialect` ) ;
126+ }
127+
68128 public escapeColumnName ( name ) {
69129 return `\`${ name } \`` ;
70130 }
0 commit comments