1
1
from sqlglot .dialects .duckdb import DuckDB as DuckdbDialect
2
2
from sqlglot .dialects .postgres import Postgres as PostgresDialect
3
3
from sqlglot .dialects .mysql import MySQL as MysqlDialect
4
+ from sqlglot .dialects .snowflake import Snowflake as SnowflakeDialect
4
5
from sqlglot import exp
5
6
from sqlglot .helper import seq_get
6
7
from sqlglot .generator import Generator
7
8
from sqlglot .dialects .dialect import (
8
9
build_date_delta ,
9
10
build_date_delta_with_interval ,
11
+ rename_func ,
12
+ unit_to_str
10
13
)
11
14
12
15
@@ -34,15 +37,75 @@ def _postgres_unix_to_time_sql(self: Generator, expression: exp.UnixToTime) -> s
34
37
return self .func ("to_timestamp" , exp .Div (this = timestamp , expression = exp .func ("POW" , 10 , scale )))
35
38
36
39
40
+ # temporary fix for Postgres IN clause(bin filter)
41
+ def _postgres_in_sql (self : Generator , expression : exp .In ) -> str :
42
+ expression .set ("expressions" , [
43
+ exp .Array (expressions = [
44
+ exp .cast (item , to = exp .DataType .Type .DOUBLE ) if isinstance (item , exp .Literal ) and item .args .get ("is_string" ) is False else item
45
+ for item in in_item_exp .args .get ("expressions" , [])
46
+ ]) if isinstance (in_item_exp , exp .Array ) else in_item_exp
47
+ for in_item_exp in expression .args .get ("expressions" , [])
48
+ ])
49
+ return self .in_sql (expression )
50
+
51
+
52
+ def _postgres_timestamp_trunc (self : Generator , expression : exp .TimestampTrunc ) -> str :
53
+ if expression .unit .this .lower () == "isoyear" :
54
+ return self .func ("to_date" , self .func ("to_char" , expression .this , exp .Literal .string ("IYYY-0001" )), exp .Literal .string ("IYYY-IDDD" ))
55
+
56
+ return self .func ("DATE_TRUNC" , unit_to_str (expression ), expression .this )
57
+
58
+
59
+ def _postgres_time_to_str_sql (self : Generator , expression : exp .TimeToStr ) -> str :
60
+ if expression .args .get ("format" ).this == "%U" :
61
+ # postgres not support non-iso week
62
+ # current_pass_days = EXTRACT(isodow FROM DATE_TRUNC('year', date))
63
+ # week_number = floor((EXTRACT(day from date) + current_pass_days - 1) / 7)
64
+ return self .sql (exp .Floor (
65
+ this = exp .Div (
66
+ this = exp .Paren (this = exp .Add (
67
+ this = exp .Sub (
68
+ this = exp .Cast (this = self .func ("TO_CHAR" , expression .this , exp .Literal .string ("DDD" )), to = "int" ),
69
+ expression = exp .Literal .number (1 )
70
+ ),
71
+ expression = exp .Extract (this = exp .Var (this = "isodow" ), expression = exp .TimestampTrunc (this = expression .this , unit = exp .Literal .string ("year" )))
72
+ )),
73
+ expression = exp .Literal .number (7 ),
74
+ )
75
+ ))
76
+
77
+ return self .func ("TO_CHAR" , expression .this , self .format_time (expression ))
78
+
79
+
80
+ def _postgres_str_to_time_sql (self : Generator , expression : exp .StrToTime ) -> str :
81
+ # adapter duckdb non-iso week
82
+ if expression .args .get ("format" ).this == "%Y%U" and isinstance (expression .this , exp .TimeToStr ) and expression .this .args .get ("format" ).this == "%Y%U" :
83
+ return self .sql (exp .Sub (
84
+ this = exp .TimestampTrunc (this = expression .this .this , unit = exp .Literal .string ("day" )),
85
+ expression = exp .Mul (
86
+ this = exp .Extract (this = exp .Var (this = "dow" ), expression = expression .this .this ),
87
+ expression = exp .Interval (this = exp .Literal .number (1 ), unit = exp .Var (this = "day" ))
88
+ )
89
+ ))
90
+ return self .func ("TO_TIMESTAMP" , expression .this , self .format_time (expression ))
91
+
92
+
37
93
PostgresDialect .Generator .TRANSFORMS [exp .Round ] = lambda _ , e : _postgres_round_generator (e )
38
94
PostgresDialect .Generator .TRANSFORMS [exp .UnixToTime ] = _postgres_unix_to_time_sql
95
+ PostgresDialect .Generator .TRANSFORMS [exp .In ] = _postgres_in_sql
96
+ PostgresDialect .Generator .TRANSFORMS [exp .TimestampTrunc ] = _postgres_timestamp_trunc
97
+ PostgresDialect .Generator .TRANSFORMS [exp .TimeToStr ] = _postgres_time_to_str_sql
98
+ PostgresDialect .Generator .TRANSFORMS [exp .StrToTime ] = _postgres_str_to_time_sql
39
99
40
100
41
101
# Mysql Dialect
42
102
def _mysql_timestamptrunc_sql (self : Generator , expression : exp .TimestampTrunc ) -> str :
43
103
unit = expression .args .get ("unit" )
44
104
45
- start_ts = "'0001-01-01 00:00:00'"
105
+ if unit .this .lower () == "isoyear" :
106
+ unit = exp .Var (this = "YEAR" )
107
+
108
+ start_ts = "'0006-01-01 00:00:00'"
46
109
47
110
timestamp_diff = build_date_delta (exp .TimestampDiff )([unit , start_ts , expression .this ])
48
111
interval = exp .Interval (this = timestamp_diff , unit = unit )
@@ -57,6 +120,13 @@ def _mysql_extract_sql(self: Generator, expression: exp.Extract) -> str:
57
120
return self .sql (exp .Sub (this = self .func ("DAYOFWEEK" , expression .expression ), expression = exp .Literal .number (1 )))
58
121
if unit == "week" :
59
122
return self .func ("WEEK" , expression .expression , exp .Literal .number (3 ))
123
+ if unit == "isoyear" :
124
+ return self .sql (exp .Floor (this = exp .Div (this = self .func ("YEARWEEK" , expression .expression , exp .Literal .number (3 )), expression = exp .Literal .number (100 ))))
125
+ if unit == "isodow" :
126
+ return self .sql (exp .Add (
127
+ this = exp .Mod (this = exp .Add (this = self .func ("DAYOFWEEK" , expression .expression ), expression = exp .Literal .number (5 )), expression = exp .Literal .number (7 )),
128
+ expression = exp .Literal .number (1 )
129
+ ))
60
130
return self .extract_sql (expression )
61
131
62
132
@@ -67,7 +137,93 @@ def _mysql_unix_to_time_sql(self: Generator, expression: exp.UnixToTime) -> str:
67
137
return self .func ("FROM_UNIXTIME" , exp .Div (this = timestamp , expression = exp .func ("POW" , 10 , scale )), self .format_time (expression ))
68
138
69
139
140
+ def _mysql_str_to_time_sql (self : Generator , expression : exp .StrToTime ) -> str :
141
+ # adapter duckdb non-iso week
142
+ if expression .args .get ("format" ).this == "%Y%U" and isinstance (expression .this , exp .TimeToStr ) and expression .this .args .get ("format" ).this == "%Y%U" :
143
+ return _mysql_timestamptrunc_sql (self , exp .TimestampTrunc (this = expression .this .this , unit = exp .Literal .string ("WEEK" )))
144
+ return self .func ("STR_TO_DATE" , expression .this , self .format_time (expression ))
145
+
146
+
70
147
MysqlDialect .Generator .TRANSFORMS [exp .Extract ] = _mysql_extract_sql
71
148
MysqlDialect .Generator .TRANSFORMS [exp .Array ] = lambda self , e : self .func ("JSON_ARRAY" , * e .expressions )
72
149
MysqlDialect .Generator .TRANSFORMS [exp .TimestampTrunc ] = _mysql_timestamptrunc_sql
73
150
MysqlDialect .Generator .TRANSFORMS [exp .UnixToTime ] = _mysql_unix_to_time_sql
151
+ MysqlDialect .Generator .TRANSFORMS [exp .Mod ] = lambda self , e : self .func ("MOD" , e .this , e .expression )
152
+ MysqlDialect .Generator .TRANSFORMS [exp .StrToTime ] = _mysql_str_to_time_sql
153
+
154
+
155
+ # Snowflake Dialect
156
+ def _snowflake_extract_sql (self : Generator , expression : exp .Extract ) -> str :
157
+ unit = expression .this .this .lower ()
158
+ if unit == "isoyear" :
159
+ return self .func ("YEAROFWEEKISO" , expression .expression )
160
+ if unit == "week" :
161
+ return self .func ("WEEKISO" , expression .expression )
162
+ if unit == "isodow" :
163
+ return self .func ("DAYOFWEEKISO" , expression .expression )
164
+ if unit == "dow" :
165
+ return exp .Sub (this = self .func ("DAYOFWEEK" , expression .expression ), expression = exp .Literal .number (1 ))
166
+ return rename_func ("DATE_PART" )(self , expression )
167
+
168
+
169
+ def _snowflake_time_to_str (self : Generator , expression : exp .TimeToStr ) -> str :
170
+ if expression .args .get ("format" ).this == "%U" :
171
+ # snowflake not support non-iso week
172
+ # IFF(TO_CHAR(TO_TIMESTAMP_TZ(TO_CHAR(date, 'YYYY'), 'YYYY'), 'DY') = 'Sun', WEEK(date), WEEK(date)-1)
173
+ return self .func (
174
+ "IFF" ,
175
+ exp .EQ (
176
+ this = self .func ("TO_CHAR" , self .func ("TO_TIMESTAMP_TZ" , self .func ("TO_CHAR" , expression .this , exp .Literal .string ('YYYY' )), exp .Literal .string ('YYYY' )), exp .Literal .string ("DY" )),
177
+ expression = exp .Literal .string ('Sun' )
178
+ ),
179
+ self .func ("WEEK" , expression .this ),
180
+ exp .Sub (this = self .func ("WEEK" , expression .this ), expression = exp .Literal .number (1 ))
181
+ )
182
+
183
+ return self .func ("TO_CHAR" , exp .cast (expression .this , exp .DataType .Type .TIMESTAMP ), self .format_time (expression ))
184
+
185
+
186
+ def _snowflake_str_to_time_sql (self : Generator , expression : exp .StrToTime ) -> str :
187
+ # adapter duckdb non-iso week
188
+ if expression .args .get ("format" ).this == "%Y%U" and isinstance (expression .this , exp .TimeToStr ) and expression .this .args .get ("format" ).this == "%Y%U" :
189
+ return self .func ("DATE_TRUNC" , exp .Literal .string ("WEEK" ), expression .this .this )
190
+ return self .func ("TO_TIMESTAMP" , expression .this , self .format_time (expression ))
191
+
192
+
193
+ def _snowflake_timestamp_trunc_sql (self : Generator , expression : exp .TimestampTrunc ) -> str :
194
+ unit = expression .unit .this .lower ()
195
+
196
+ # dateadd(day, -((date_extract(DAYOFWEEKISO from date)) - 1), date_trunc('day', date))
197
+ trunc_iso_week = self .func (
198
+ "dateadd" ,
199
+ exp .Var (this = "day" ),
200
+ exp .Sub (
201
+ this = exp .Literal .number (1 ),
202
+ expression = exp .Extract (this = exp .Var (this = "DAYOFWEEKISO" ), expression = expression .this )
203
+ ),
204
+ self .func ("date_trunc" , exp .Literal .string ("day" ), expression .this )
205
+ )
206
+
207
+ # dateadd(week, 1-(WEEKISO(date)), trunc_iso_week)
208
+ if unit == "isoyear" :
209
+ return self .func (
210
+ "dateadd" ,
211
+ exp .Var (this = "week" ),
212
+ exp .Sub (
213
+ this = exp .Literal .number (1 ),
214
+ expression = self .func ("WEEKISO" , expression .this )
215
+ ),
216
+ trunc_iso_week
217
+ )
218
+
219
+ # duckdb week means "isoweek"
220
+ if unit == "week" :
221
+ return trunc_iso_week
222
+
223
+ return self .func ("DATE_TRUNC" , expression .unit , expression .this )
224
+
225
+
226
+ SnowflakeDialect .Generator .TRANSFORMS [exp .Extract ] = _snowflake_extract_sql
227
+ SnowflakeDialect .Generator .TRANSFORMS [exp .TimeToStr ] = _snowflake_time_to_str
228
+ SnowflakeDialect .Generator .TRANSFORMS [exp .StrToTime ] = _snowflake_str_to_time_sql
229
+ SnowflakeDialect .Generator .TRANSFORMS [exp .TimestampTrunc ] = _snowflake_timestamp_trunc_sql
0 commit comments