1+ import { parseSqlInterval } from '@cubejs-backend/shared' ;
12import { PostgresQuery } from './PostgresQuery' ;
23
34export class RedshiftQuery extends PostgresQuery {
@@ -12,6 +13,74 @@ export class RedshiftQuery extends PostgresQuery {
1213 return 'GETDATE()' ;
1314 }
1415
16+ /**
17+ * Redshift doesn't support Interval values with month or year parts (as Postgres does)
18+ * so we need to make date math on our own.
19+ */
20+ public override subtractInterval ( date : string , interval : string ) : string {
21+ const intervalParsed = parseSqlInterval ( interval ) ;
22+ let result = date ;
23+
24+ for ( const [ datePart , intervalValue ] of Object . entries ( intervalParsed ) ) {
25+ result = `DATEADD(${ datePart } , -${ intervalValue } , ${ result } )` ;
26+ }
27+
28+ return result ;
29+ }
30+
31+ /**
32+ * Redshift doesn't support Interval values with month or year parts (as Postgres does)
33+ * so we need to make date math on our own.
34+ */
35+ public override addInterval ( date : string , interval : string ) : string {
36+ const intervalParsed = parseSqlInterval ( interval ) ;
37+ let result = date ;
38+
39+ for ( const [ datePart , intervalValue ] of Object . entries ( intervalParsed ) ) {
40+ result = `DATEADD(${ datePart } , ${ intervalValue } , ${ result } )` ;
41+ }
42+
43+ return result ;
44+ }
45+
46+ /**
47+ * Redshift doesn't support Interval values with month or year parts (as Postgres does)
48+ * so we need to make date math on our own.
49+ */
50+ public override dateBin ( interval : string , source : string , origin : string ) : string {
51+ const intervalParsed = parseSqlInterval ( interval ) ;
52+
53+ if ( ( intervalParsed . year || intervalParsed . month || intervalParsed . quarter ) &&
54+ ( intervalParsed . week || intervalParsed . day || intervalParsed . hour || intervalParsed . minute || intervalParsed . second ) ) {
55+ throw new Error ( `Complex intervals like "${ interval } " are not supported. Please use Year to Month or Day to second intervals` ) ;
56+ }
57+
58+ if ( intervalParsed . year || intervalParsed . month || intervalParsed . quarter ) {
59+ let totalMonths = 0 ;
60+
61+ if ( intervalParsed . year ) {
62+ totalMonths += intervalParsed . year * 12 ;
63+ }
64+
65+ if ( intervalParsed . quarter ) {
66+ totalMonths += intervalParsed . quarter * 3 ;
67+ }
68+
69+ if ( intervalParsed . month ) {
70+ totalMonths += intervalParsed . month ;
71+ }
72+
73+ return `DATEADD(
74+ month,
75+ (FLOOR(DATEDIFF(month, ${ this . dateTimeCast ( `'${ origin } '` ) } , ${ source } ) / ${ totalMonths } ) * ${ totalMonths } )::int,
76+ ${ this . dateTimeCast ( `'${ origin } '` ) }
77+ )` ;
78+ }
79+
80+ // For days and lower intervals - we can reuse Postgres version
81+ return super . dateBin ( interval , source , origin ) ;
82+ }
83+
1584 public sqlTemplates ( ) {
1685 const templates = super . sqlTemplates ( ) ;
1786 templates . functions . DLOG10 = 'LOG(10, {{ args_concat }})' ;
0 commit comments