sqlglot 27.29.0__py3-none-any.whl → 28.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sqlglot/__main__.py +6 -4
- sqlglot/_version.py +2 -2
- sqlglot/dialects/bigquery.py +116 -295
- sqlglot/dialects/clickhouse.py +67 -2
- sqlglot/dialects/databricks.py +38 -1
- sqlglot/dialects/dialect.py +327 -286
- sqlglot/dialects/dremio.py +4 -1
- sqlglot/dialects/duckdb.py +718 -22
- sqlglot/dialects/exasol.py +243 -10
- sqlglot/dialects/hive.py +8 -8
- sqlglot/dialects/mysql.py +11 -2
- sqlglot/dialects/oracle.py +29 -0
- sqlglot/dialects/postgres.py +46 -24
- sqlglot/dialects/presto.py +47 -16
- sqlglot/dialects/redshift.py +16 -0
- sqlglot/dialects/risingwave.py +3 -0
- sqlglot/dialects/singlestore.py +12 -3
- sqlglot/dialects/snowflake.py +199 -271
- sqlglot/dialects/spark.py +2 -2
- sqlglot/dialects/spark2.py +11 -48
- sqlglot/dialects/sqlite.py +9 -0
- sqlglot/dialects/teradata.py +5 -8
- sqlglot/dialects/trino.py +6 -0
- sqlglot/dialects/tsql.py +61 -25
- sqlglot/diff.py +4 -2
- sqlglot/errors.py +69 -0
- sqlglot/expressions.py +484 -84
- sqlglot/generator.py +143 -41
- sqlglot/helper.py +2 -2
- sqlglot/optimizer/annotate_types.py +247 -140
- sqlglot/optimizer/canonicalize.py +6 -1
- sqlglot/optimizer/eliminate_joins.py +1 -1
- sqlglot/optimizer/eliminate_subqueries.py +2 -2
- sqlglot/optimizer/merge_subqueries.py +5 -5
- sqlglot/optimizer/normalize.py +20 -13
- sqlglot/optimizer/normalize_identifiers.py +17 -3
- sqlglot/optimizer/optimizer.py +4 -0
- sqlglot/optimizer/pushdown_predicates.py +1 -1
- sqlglot/optimizer/qualify.py +14 -6
- sqlglot/optimizer/qualify_columns.py +113 -352
- sqlglot/optimizer/qualify_tables.py +112 -70
- sqlglot/optimizer/resolver.py +374 -0
- sqlglot/optimizer/scope.py +27 -16
- sqlglot/optimizer/simplify.py +1074 -964
- sqlglot/optimizer/unnest_subqueries.py +12 -2
- sqlglot/parser.py +276 -160
- sqlglot/planner.py +2 -2
- sqlglot/schema.py +15 -4
- sqlglot/tokens.py +42 -7
- sqlglot/transforms.py +77 -22
- sqlglot/typing/__init__.py +316 -0
- sqlglot/typing/bigquery.py +376 -0
- sqlglot/typing/hive.py +12 -0
- sqlglot/typing/presto.py +24 -0
- sqlglot/typing/snowflake.py +505 -0
- sqlglot/typing/spark2.py +58 -0
- sqlglot/typing/tsql.py +9 -0
- {sqlglot-27.29.0.dist-info → sqlglot-28.4.1.dist-info}/METADATA +2 -2
- sqlglot-28.4.1.dist-info/RECORD +92 -0
- sqlglot-27.29.0.dist-info/RECORD +0 -84
- {sqlglot-27.29.0.dist-info → sqlglot-28.4.1.dist-info}/WHEEL +0 -0
- {sqlglot-27.29.0.dist-info → sqlglot-28.4.1.dist-info}/licenses/LICENSE +0 -0
- {sqlglot-27.29.0.dist-info → sqlglot-28.4.1.dist-info}/top_level.txt +0 -0
sqlglot/generator.py
CHANGED
|
@@ -80,7 +80,7 @@ class Generator(metaclass=_Generator):
|
|
|
80
80
|
Default: False.
|
|
81
81
|
identify: Determines when an identifier should be quoted. Possible values are:
|
|
82
82
|
False (default): Never quote, except in cases where it's mandatory by the dialect.
|
|
83
|
-
True
|
|
83
|
+
True: Always quote except for specials cases.
|
|
84
84
|
'safe': Only quote identifiers that are case insensitive.
|
|
85
85
|
normalize: Whether to normalize identifiers to lowercase.
|
|
86
86
|
Default: False.
|
|
@@ -137,6 +137,8 @@ class Generator(metaclass=_Generator):
|
|
|
137
137
|
exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
|
|
138
138
|
exp.CredentialsProperty: lambda self,
|
|
139
139
|
e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
|
|
140
|
+
exp.CurrentCatalog: lambda *_: "CURRENT_CATALOG",
|
|
141
|
+
exp.SessionUser: lambda *_: "SESSION_USER",
|
|
140
142
|
exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
|
|
141
143
|
exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
|
|
142
144
|
exp.DynamicProperty: lambda *_: "DYNAMIC",
|
|
@@ -177,6 +179,8 @@ class Generator(metaclass=_Generator):
|
|
|
177
179
|
exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
|
|
178
180
|
exp.Operator: lambda self, e: self.binary(e, ""), # The operator is produced in `binary`
|
|
179
181
|
exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
|
|
182
|
+
exp.ExtendsLeft: lambda self, e: self.binary(e, "&<"),
|
|
183
|
+
exp.ExtendsRight: lambda self, e: self.binary(e, "&>"),
|
|
180
184
|
exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
|
|
181
185
|
exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
|
|
182
186
|
exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
|
|
@@ -184,6 +188,7 @@ class Generator(metaclass=_Generator):
|
|
|
184
188
|
exp.PositionalColumn: lambda self, e: f"#{self.sql(e, 'this')}",
|
|
185
189
|
exp.ProjectionPolicyColumnConstraint: lambda self,
|
|
186
190
|
e: f"PROJECTION POLICY {self.sql(e, 'this')}",
|
|
191
|
+
exp.ZeroFillColumnConstraint: lambda self, e: "ZEROFILL",
|
|
187
192
|
exp.Put: lambda self, e: self.get_put_sql(e),
|
|
188
193
|
exp.RemoteWithConnectionModelProperty: lambda self,
|
|
189
194
|
e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
|
|
@@ -198,8 +203,7 @@ class Generator(metaclass=_Generator):
|
|
|
198
203
|
exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
|
|
199
204
|
exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
|
|
200
205
|
exp.SqlReadWriteProperty: lambda _, e: e.name,
|
|
201
|
-
exp.SqlSecurityProperty: lambda
|
|
202
|
-
e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
|
|
206
|
+
exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {self.sql(e, 'this')}",
|
|
203
207
|
exp.StabilityProperty: lambda _, e: e.name,
|
|
204
208
|
exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
|
|
205
209
|
exp.StreamingTableProperty: lambda *_: "STREAMING",
|
|
@@ -217,7 +221,6 @@ class Generator(metaclass=_Generator):
|
|
|
217
221
|
exp.UnloggedProperty: lambda *_: "UNLOGGED",
|
|
218
222
|
exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
|
|
219
223
|
exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
|
|
220
|
-
exp.Uuid: lambda *_: "UUID()",
|
|
221
224
|
exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
|
|
222
225
|
exp.UtcDate: lambda self, e: self.sql(exp.CurrentDate(this=exp.Literal.string("UTC"))),
|
|
223
226
|
exp.UtcTime: lambda self, e: self.sql(exp.CurrentTime(this=exp.Literal.string("UTC"))),
|
|
@@ -227,7 +230,6 @@ class Generator(metaclass=_Generator):
|
|
|
227
230
|
exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
|
|
228
231
|
exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
|
|
229
232
|
exp.VolatileProperty: lambda *_: "VOLATILE",
|
|
230
|
-
exp.WeekStart: lambda self, e: f"WEEK({self.sql(e, 'this')})",
|
|
231
233
|
exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
|
|
232
234
|
exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
|
|
233
235
|
exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
|
|
@@ -509,6 +511,9 @@ class Generator(metaclass=_Generator):
|
|
|
509
511
|
# Prefix which is appended to exp.Table expressions in MATCH AGAINST
|
|
510
512
|
MATCH_AGAINST_TABLE_PREFIX: t.Optional[str] = None
|
|
511
513
|
|
|
514
|
+
# Whether to include the VARIABLE keyword for SET assignments
|
|
515
|
+
SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD = False
|
|
516
|
+
|
|
512
517
|
TYPE_MAPPING = {
|
|
513
518
|
exp.DataType.Type.DATETIME2: "TIMESTAMP",
|
|
514
519
|
exp.DataType.Type.NCHAR: "CHAR",
|
|
@@ -1028,6 +1033,9 @@ class Generator(metaclass=_Generator):
|
|
|
1028
1033
|
|
|
1029
1034
|
return f"{self.column_parts(expression)}{join_mark}"
|
|
1030
1035
|
|
|
1036
|
+
def pseudocolumn_sql(self, expression: exp.Pseudocolumn) -> str:
|
|
1037
|
+
return self.column_sql(expression)
|
|
1038
|
+
|
|
1031
1039
|
def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
|
|
1032
1040
|
this = self.sql(expression, "this")
|
|
1033
1041
|
this = f" {this}" if this else ""
|
|
@@ -1302,7 +1310,7 @@ class Generator(metaclass=_Generator):
|
|
|
1302
1310
|
return f"${tag}${self.sql(expression, 'this')}${tag}$"
|
|
1303
1311
|
|
|
1304
1312
|
def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
|
|
1305
|
-
with_ = self.sql(expression, "
|
|
1313
|
+
with_ = self.sql(expression, "with_")
|
|
1306
1314
|
if with_:
|
|
1307
1315
|
sql = f"{with_}{self.sep()}{sql}"
|
|
1308
1316
|
return sql
|
|
@@ -1391,7 +1399,20 @@ class Generator(metaclass=_Generator):
|
|
|
1391
1399
|
delimiter=self.dialect.BYTE_END,
|
|
1392
1400
|
escaped_delimiter=self._escaped_byte_quote_end,
|
|
1393
1401
|
)
|
|
1394
|
-
|
|
1402
|
+
is_bytes = expression.args.get("is_bytes", False)
|
|
1403
|
+
delimited_byte_string = (
|
|
1404
|
+
f"{self.dialect.BYTE_START}{escaped_byte_string}{self.dialect.BYTE_END}"
|
|
1405
|
+
)
|
|
1406
|
+
if is_bytes and not self.dialect.BYTE_STRING_IS_BYTES_TYPE:
|
|
1407
|
+
return self.sql(
|
|
1408
|
+
exp.cast(delimited_byte_string, exp.DataType.Type.BINARY, dialect=self.dialect)
|
|
1409
|
+
)
|
|
1410
|
+
if not is_bytes and self.dialect.BYTE_STRING_IS_BYTES_TYPE:
|
|
1411
|
+
return self.sql(
|
|
1412
|
+
exp.cast(delimited_byte_string, exp.DataType.Type.VARCHAR, dialect=self.dialect)
|
|
1413
|
+
)
|
|
1414
|
+
|
|
1415
|
+
return delimited_byte_string
|
|
1395
1416
|
return this
|
|
1396
1417
|
|
|
1397
1418
|
def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
|
|
@@ -1487,13 +1508,14 @@ class Generator(metaclass=_Generator):
|
|
|
1487
1508
|
cluster = f" {cluster}" if cluster else ""
|
|
1488
1509
|
where = self.sql(expression, "where")
|
|
1489
1510
|
returning = self.sql(expression, "returning")
|
|
1511
|
+
order = self.sql(expression, "order")
|
|
1490
1512
|
limit = self.sql(expression, "limit")
|
|
1491
1513
|
tables = self.expressions(expression, key="tables")
|
|
1492
1514
|
tables = f" {tables}" if tables else ""
|
|
1493
1515
|
if self.RETURNING_END:
|
|
1494
|
-
expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
|
|
1516
|
+
expression_sql = f"{this}{using}{cluster}{where}{returning}{order}{limit}"
|
|
1495
1517
|
else:
|
|
1496
|
-
expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
|
|
1518
|
+
expression_sql = f"{returning}{this}{using}{cluster}{where}{order}{limit}"
|
|
1497
1519
|
return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
|
|
1498
1520
|
|
|
1499
1521
|
def drop_sql(self, expression: exp.Drop) -> str:
|
|
@@ -1660,7 +1682,7 @@ class Generator(metaclass=_Generator):
|
|
|
1660
1682
|
text = text.replace(self._identifier_end, self._escaped_identifier_end)
|
|
1661
1683
|
if (
|
|
1662
1684
|
expression.quoted
|
|
1663
|
-
or self.dialect.
|
|
1685
|
+
or self.dialect.can_quote(expression, self.identify)
|
|
1664
1686
|
or lower in self.RESERVED_KEYWORDS
|
|
1665
1687
|
or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
|
|
1666
1688
|
):
|
|
@@ -1927,7 +1949,7 @@ class Generator(metaclass=_Generator):
|
|
|
1927
1949
|
|
|
1928
1950
|
sql = f"SYSTEM_VERSIONING={on_sql}"
|
|
1929
1951
|
|
|
1930
|
-
return f"WITH({sql})" if expression.args.get("
|
|
1952
|
+
return f"WITH({sql})" if expression.args.get("with_") else sql
|
|
1931
1953
|
|
|
1932
1954
|
def insert_sql(self, expression: exp.Insert) -> str:
|
|
1933
1955
|
hint = self.sql(expression, "hint")
|
|
@@ -2100,7 +2122,13 @@ class Generator(metaclass=_Generator):
|
|
|
2100
2122
|
if rows_from:
|
|
2101
2123
|
table = f"ROWS FROM {self.wrap(rows_from)}"
|
|
2102
2124
|
|
|
2103
|
-
|
|
2125
|
+
indexed = expression.args.get("indexed")
|
|
2126
|
+
if indexed is not None:
|
|
2127
|
+
indexed = f" INDEXED BY {self.sql(indexed)}" if indexed else " NOT INDEXED"
|
|
2128
|
+
else:
|
|
2129
|
+
indexed = ""
|
|
2130
|
+
|
|
2131
|
+
return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{indexed}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
|
|
2104
2132
|
|
|
2105
2133
|
def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
|
|
2106
2134
|
table = self.func("TABLE", expression.this)
|
|
@@ -2151,14 +2179,15 @@ class Generator(metaclass=_Generator):
|
|
|
2151
2179
|
if expression.this:
|
|
2152
2180
|
this = self.sql(expression, "this")
|
|
2153
2181
|
if not expressions:
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2182
|
+
sql = f"UNPIVOT {this}"
|
|
2183
|
+
else:
|
|
2184
|
+
on = f"{self.seg('ON')} {expressions}"
|
|
2185
|
+
into = self.sql(expression, "into")
|
|
2186
|
+
into = f"{self.seg('INTO')} {into}" if into else ""
|
|
2187
|
+
using = self.expressions(expression, key="using", flat=True)
|
|
2188
|
+
using = f"{self.seg('USING')} {using}" if using else ""
|
|
2189
|
+
sql = f"{direction} {this}{on}{into}{using}{group}"
|
|
2190
|
+
return self.prepend_ctes(expression, sql)
|
|
2162
2191
|
|
|
2163
2192
|
alias = self.sql(expression, "alias")
|
|
2164
2193
|
alias = f" AS {alias}" if alias else ""
|
|
@@ -2181,7 +2210,8 @@ class Generator(metaclass=_Generator):
|
|
|
2181
2210
|
|
|
2182
2211
|
default_on_null = self.sql(expression, "default_on_null")
|
|
2183
2212
|
default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
|
|
2184
|
-
|
|
2213
|
+
sql = f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
|
|
2214
|
+
return self.prepend_ctes(expression, sql)
|
|
2185
2215
|
|
|
2186
2216
|
def version_sql(self, expression: exp.Version) -> str:
|
|
2187
2217
|
this = f"FOR {expression.name}"
|
|
@@ -2195,7 +2225,7 @@ class Generator(metaclass=_Generator):
|
|
|
2195
2225
|
def update_sql(self, expression: exp.Update) -> str:
|
|
2196
2226
|
this = self.sql(expression, "this")
|
|
2197
2227
|
set_sql = self.expressions(expression, flat=True)
|
|
2198
|
-
from_sql = self.sql(expression, "
|
|
2228
|
+
from_sql = self.sql(expression, "from_")
|
|
2199
2229
|
where_sql = self.sql(expression, "where")
|
|
2200
2230
|
returning = self.sql(expression, "returning")
|
|
2201
2231
|
order = self.sql(expression, "order")
|
|
@@ -2334,7 +2364,7 @@ class Generator(metaclass=_Generator):
|
|
|
2334
2364
|
op
|
|
2335
2365
|
for op in (
|
|
2336
2366
|
expression.method,
|
|
2337
|
-
"GLOBAL" if expression.args.get("
|
|
2367
|
+
"GLOBAL" if expression.args.get("global_") else None,
|
|
2338
2368
|
side,
|
|
2339
2369
|
expression.kind,
|
|
2340
2370
|
expression.hint if self.JOIN_HINTS else None,
|
|
@@ -2441,12 +2471,15 @@ class Generator(metaclass=_Generator):
|
|
|
2441
2471
|
|
|
2442
2472
|
def setitem_sql(self, expression: exp.SetItem) -> str:
|
|
2443
2473
|
kind = self.sql(expression, "kind")
|
|
2444
|
-
|
|
2474
|
+
if not self.SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD and kind == "VARIABLE":
|
|
2475
|
+
kind = ""
|
|
2476
|
+
else:
|
|
2477
|
+
kind = f"{kind} " if kind else ""
|
|
2445
2478
|
this = self.sql(expression, "this")
|
|
2446
2479
|
expressions = self.expressions(expression)
|
|
2447
2480
|
collate = self.sql(expression, "collate")
|
|
2448
2481
|
collate = f" COLLATE {collate}" if collate else ""
|
|
2449
|
-
global_ = "GLOBAL " if expression.args.get("
|
|
2482
|
+
global_ = "GLOBAL " if expression.args.get("global_") else ""
|
|
2450
2483
|
return f"{global_}{kind}{this}{expressions}{collate}"
|
|
2451
2484
|
|
|
2452
2485
|
def set_sql(self, expression: exp.Set) -> str:
|
|
@@ -2544,7 +2577,7 @@ class Generator(metaclass=_Generator):
|
|
|
2544
2577
|
return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore
|
|
2545
2578
|
|
|
2546
2579
|
def withfill_sql(self, expression: exp.WithFill) -> str:
|
|
2547
|
-
from_sql = self.sql(expression, "
|
|
2580
|
+
from_sql = self.sql(expression, "from_")
|
|
2548
2581
|
from_sql = f" FROM {from_sql}" if from_sql else ""
|
|
2549
2582
|
to_sql = self.sql(expression, "to")
|
|
2550
2583
|
to_sql = f" TO {to_sql}" if to_sql else ""
|
|
@@ -2705,7 +2738,7 @@ class Generator(metaclass=_Generator):
|
|
|
2705
2738
|
return f" {options}" if options else ""
|
|
2706
2739
|
|
|
2707
2740
|
def for_modifiers(self, expression: exp.Expression) -> str:
|
|
2708
|
-
for_modifiers = self.expressions(expression, key="
|
|
2741
|
+
for_modifiers = self.expressions(expression, key="for_")
|
|
2709
2742
|
return f"{self.sep()}FOR XML{self.seg(for_modifiers)}" if for_modifiers else ""
|
|
2710
2743
|
|
|
2711
2744
|
def queryoption_sql(self, expression: exp.QueryOption) -> str:
|
|
@@ -2776,11 +2809,11 @@ class Generator(metaclass=_Generator):
|
|
|
2776
2809
|
expression,
|
|
2777
2810
|
f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
|
|
2778
2811
|
self.sql(expression, "into", comment=False),
|
|
2779
|
-
self.sql(expression, "
|
|
2812
|
+
self.sql(expression, "from_", comment=False),
|
|
2780
2813
|
)
|
|
2781
2814
|
|
|
2782
2815
|
# If both the CTE and SELECT clauses have comments, generate the latter earlier
|
|
2783
|
-
if expression.args.get("
|
|
2816
|
+
if expression.args.get("with_"):
|
|
2784
2817
|
sql = self.maybe_comment(sql, expression)
|
|
2785
2818
|
expression.pop_comments()
|
|
2786
2819
|
|
|
@@ -2808,7 +2841,7 @@ class Generator(metaclass=_Generator):
|
|
|
2808
2841
|
return ""
|
|
2809
2842
|
|
|
2810
2843
|
def star_sql(self, expression: exp.Star) -> str:
|
|
2811
|
-
except_ = self.expressions(expression, key="
|
|
2844
|
+
except_ = self.expressions(expression, key="except_", flat=True)
|
|
2812
2845
|
except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
|
|
2813
2846
|
replace = self.expressions(expression, key="replace", flat=True)
|
|
2814
2847
|
replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
|
|
@@ -3068,6 +3101,14 @@ class Generator(metaclass=_Generator):
|
|
|
3068
3101
|
return args
|
|
3069
3102
|
|
|
3070
3103
|
def concat_sql(self, expression: exp.Concat) -> str:
|
|
3104
|
+
if self.dialect.CONCAT_COALESCE and not expression.args.get("coalesce"):
|
|
3105
|
+
# Dialect's CONCAT function coalesces NULLs to empty strings, but the expression does not.
|
|
3106
|
+
# Transpile to double pipe operators, which typically returns NULL if any args are NULL
|
|
3107
|
+
# instead of coalescing them to empty string.
|
|
3108
|
+
from sqlglot.dialects.dialect import concat_to_dpipe_sql
|
|
3109
|
+
|
|
3110
|
+
return concat_to_dpipe_sql(self, expression)
|
|
3111
|
+
|
|
3071
3112
|
expressions = self.convert_concat_args(expression)
|
|
3072
3113
|
|
|
3073
3114
|
# Some dialects don't allow a single-argument CONCAT call
|
|
@@ -3099,11 +3140,13 @@ class Generator(metaclass=_Generator):
|
|
|
3099
3140
|
return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
|
|
3100
3141
|
|
|
3101
3142
|
def primarykey_sql(self, expression: exp.PrimaryKey) -> str:
|
|
3143
|
+
this = self.sql(expression, "this")
|
|
3144
|
+
this = f" {this}" if this else ""
|
|
3102
3145
|
expressions = self.expressions(expression, flat=True)
|
|
3103
3146
|
include = self.sql(expression, "include")
|
|
3104
3147
|
options = self.expressions(expression, key="options", flat=True, sep=" ")
|
|
3105
3148
|
options = f" {options}" if options else ""
|
|
3106
|
-
return f"PRIMARY KEY ({expressions}){include}{options}"
|
|
3149
|
+
return f"PRIMARY KEY{this} ({expressions}){include}{options}"
|
|
3107
3150
|
|
|
3108
3151
|
def if_sql(self, expression: exp.If) -> str:
|
|
3109
3152
|
return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
|
|
@@ -3801,6 +3844,10 @@ class Generator(metaclass=_Generator):
|
|
|
3801
3844
|
for expr in exprs[1:]:
|
|
3802
3845
|
like_expr = connective(like_expr, exp_class(this=this, expression=expr))
|
|
3803
3846
|
|
|
3847
|
+
parent = expression.parent
|
|
3848
|
+
if not isinstance(parent, type(like_expr)) and isinstance(parent, exp.Condition):
|
|
3849
|
+
like_expr = exp.paren(like_expr, copy=False)
|
|
3850
|
+
|
|
3804
3851
|
return self.sql(like_expr)
|
|
3805
3852
|
|
|
3806
3853
|
return self.binary(expression, op)
|
|
@@ -3811,6 +3858,9 @@ class Generator(metaclass=_Generator):
|
|
|
3811
3858
|
def ilike_sql(self, expression: exp.ILike) -> str:
|
|
3812
3859
|
return self._like_sql(expression)
|
|
3813
3860
|
|
|
3861
|
+
def match_sql(self, expression: exp.Match) -> str:
|
|
3862
|
+
return self.binary(expression, "MATCH")
|
|
3863
|
+
|
|
3814
3864
|
def similarto_sql(self, expression: exp.SimilarTo) -> str:
|
|
3815
3865
|
return self.binary(expression, "SIMILAR TO")
|
|
3816
3866
|
|
|
@@ -3835,9 +3885,6 @@ class Generator(metaclass=_Generator):
|
|
|
3835
3885
|
def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
|
|
3836
3886
|
return self.binary(expression, "IS DISTINCT FROM")
|
|
3837
3887
|
|
|
3838
|
-
def slice_sql(self, expression: exp.Slice) -> str:
|
|
3839
|
-
return self.binary(expression, ":")
|
|
3840
|
-
|
|
3841
3888
|
def sub_sql(self, expression: exp.Sub) -> str:
|
|
3842
3889
|
return self.binary(expression, "-")
|
|
3843
3890
|
|
|
@@ -4065,7 +4112,9 @@ class Generator(metaclass=_Generator):
|
|
|
4065
4112
|
if isinstance(then_expression.args.get("expressions"), exp.Star):
|
|
4066
4113
|
then = f"UPDATE {self.sql(then_expression, 'expressions')}"
|
|
4067
4114
|
else:
|
|
4068
|
-
|
|
4115
|
+
expressions_sql = self.expressions(then_expression)
|
|
4116
|
+
then = f"UPDATE SET{self.sep()}{expressions_sql}" if expressions_sql else "UPDATE"
|
|
4117
|
+
|
|
4069
4118
|
else:
|
|
4070
4119
|
then = self.sql(then_expression)
|
|
4071
4120
|
return f"WHEN {matched}{source}{condition} THEN {then}"
|
|
@@ -4336,8 +4385,8 @@ class Generator(metaclass=_Generator):
|
|
|
4336
4385
|
|
|
4337
4386
|
def refresh_sql(self, expression: exp.Refresh) -> str:
|
|
4338
4387
|
this = self.sql(expression, "this")
|
|
4339
|
-
|
|
4340
|
-
return f"REFRESH {
|
|
4388
|
+
kind = "" if isinstance(expression.this, exp.Literal) else f"{expression.text('kind')} "
|
|
4389
|
+
return f"REFRESH {kind}{this}"
|
|
4341
4390
|
|
|
4342
4391
|
def toarray_sql(self, expression: exp.ToArray) -> str:
|
|
4343
4392
|
arg = expression.this
|
|
@@ -4795,7 +4844,7 @@ class Generator(metaclass=_Generator):
|
|
|
4795
4844
|
this = self.sql(expression, "this")
|
|
4796
4845
|
this = f" {this}" if this else ""
|
|
4797
4846
|
|
|
4798
|
-
_with = expression.args.get("
|
|
4847
|
+
_with = expression.args.get("with_")
|
|
4799
4848
|
|
|
4800
4849
|
if _with is None:
|
|
4801
4850
|
with_sql = ""
|
|
@@ -4905,6 +4954,14 @@ class Generator(metaclass=_Generator):
|
|
|
4905
4954
|
|
|
4906
4955
|
return array_agg
|
|
4907
4956
|
|
|
4957
|
+
def slice_sql(self, expression: exp.Slice) -> str:
|
|
4958
|
+
step = self.sql(expression, "step")
|
|
4959
|
+
end = self.sql(expression.expression)
|
|
4960
|
+
begin = self.sql(expression.this)
|
|
4961
|
+
|
|
4962
|
+
sql = f"{end}:{step}" if step else end
|
|
4963
|
+
return f"{begin}:{sql}" if sql else f"{begin}:"
|
|
4964
|
+
|
|
4908
4965
|
def apply_sql(self, expression: exp.Apply) -> str:
|
|
4909
4966
|
this = self.sql(expression, "this")
|
|
4910
4967
|
expr = self.sql(expression, "expression")
|
|
@@ -4979,8 +5036,8 @@ class Generator(metaclass=_Generator):
|
|
|
4979
5036
|
def overlay_sql(self, expression: exp.Overlay):
|
|
4980
5037
|
this = self.sql(expression, "this")
|
|
4981
5038
|
expr = self.sql(expression, "expression")
|
|
4982
|
-
from_sql = self.sql(expression, "
|
|
4983
|
-
for_sql = self.sql(expression, "
|
|
5039
|
+
from_sql = self.sql(expression, "from_")
|
|
5040
|
+
for_sql = self.sql(expression, "for_")
|
|
4984
5041
|
for_sql = f" FOR {for_sql}" if for_sql else ""
|
|
4985
5042
|
|
|
4986
5043
|
return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
|
|
@@ -5311,9 +5368,11 @@ class Generator(metaclass=_Generator):
|
|
|
5311
5368
|
expression, "metrics", dynamic=True, skip_first=True, skip_last=True
|
|
5312
5369
|
)
|
|
5313
5370
|
metrics = self.seg(f"METRICS {metrics}") if metrics else ""
|
|
5371
|
+
facts = self.expressions(expression, "facts", dynamic=True, skip_first=True, skip_last=True)
|
|
5372
|
+
facts = self.seg(f"FACTS {facts}") if facts else ""
|
|
5314
5373
|
where = self.sql(expression, "where")
|
|
5315
5374
|
where = self.seg(f"WHERE {where}") if where else ""
|
|
5316
|
-
body = self.indent(this + metrics + dimensions + where, skip_first=True)
|
|
5375
|
+
body = self.indent(this + metrics + dimensions + facts + where, skip_first=True)
|
|
5317
5376
|
return f"SEMANTIC_VIEW({body}{self.seg(')', sep='')}"
|
|
5318
5377
|
|
|
5319
5378
|
def getextract_sql(self, expression: exp.GetExtract) -> str:
|
|
@@ -5365,3 +5424,46 @@ class Generator(metaclass=_Generator):
|
|
|
5365
5424
|
|
|
5366
5425
|
def directorystage_sql(self, expression: exp.DirectoryStage) -> str:
|
|
5367
5426
|
return self.func("DIRECTORY", expression.this)
|
|
5427
|
+
|
|
5428
|
+
def uuid_sql(self, expression: exp.Uuid) -> str:
|
|
5429
|
+
is_string = expression.args.get("is_string", False)
|
|
5430
|
+
uuid_func_sql = self.func("UUID")
|
|
5431
|
+
|
|
5432
|
+
if is_string and not self.dialect.UUID_IS_STRING_TYPE:
|
|
5433
|
+
return self.sql(
|
|
5434
|
+
exp.cast(uuid_func_sql, exp.DataType.Type.VARCHAR, dialect=self.dialect)
|
|
5435
|
+
)
|
|
5436
|
+
|
|
5437
|
+
return uuid_func_sql
|
|
5438
|
+
|
|
5439
|
+
def initcap_sql(self, expression: exp.Initcap) -> str:
|
|
5440
|
+
delimiters = expression.expression
|
|
5441
|
+
|
|
5442
|
+
if delimiters:
|
|
5443
|
+
# do not generate delimiters arg if we are round-tripping from default delimiters
|
|
5444
|
+
if (
|
|
5445
|
+
delimiters.is_string
|
|
5446
|
+
and delimiters.this == self.dialect.INITCAP_DEFAULT_DELIMITER_CHARS
|
|
5447
|
+
):
|
|
5448
|
+
delimiters = None
|
|
5449
|
+
elif not self.dialect.INITCAP_SUPPORTS_CUSTOM_DELIMITERS:
|
|
5450
|
+
self.unsupported("INITCAP does not support custom delimiters")
|
|
5451
|
+
delimiters = None
|
|
5452
|
+
|
|
5453
|
+
return self.func("INITCAP", expression.this, delimiters)
|
|
5454
|
+
|
|
5455
|
+
def localtime_sql(self, expression: exp.Localtime) -> str:
|
|
5456
|
+
this = expression.this
|
|
5457
|
+
return self.func("LOCALTIME", this) if this else "LOCALTIME"
|
|
5458
|
+
|
|
5459
|
+
def localtimestamp_sql(self, expression: exp.Localtime) -> str:
|
|
5460
|
+
this = expression.this
|
|
5461
|
+
return self.func("LOCALTIMESTAMP", this) if this else "LOCALTIMESTAMP"
|
|
5462
|
+
|
|
5463
|
+
def weekstart_sql(self, expression: exp.WeekStart) -> str:
|
|
5464
|
+
this = expression.this.name.upper()
|
|
5465
|
+
if self.dialect.WEEK_OFFSET == -1 and this == "SUNDAY":
|
|
5466
|
+
# BigQuery specific optimization since WEEK(SUNDAY) == WEEK
|
|
5467
|
+
return "WEEK"
|
|
5468
|
+
|
|
5469
|
+
return self.func("WEEK", expression.this)
|
sqlglot/helper.py
CHANGED
|
@@ -139,7 +139,7 @@ def csv(*args: str, sep: str = ", ") -> str:
|
|
|
139
139
|
def subclasses(
|
|
140
140
|
module_name: str,
|
|
141
141
|
classes: t.Type | t.Tuple[t.Type, ...],
|
|
142
|
-
exclude: t.
|
|
142
|
+
exclude: t.Set[t.Type] = set(),
|
|
143
143
|
) -> t.List[t.Type]:
|
|
144
144
|
"""
|
|
145
145
|
Returns all subclasses for a collection of classes, possibly excluding some of them.
|
|
@@ -147,7 +147,7 @@ def subclasses(
|
|
|
147
147
|
Args:
|
|
148
148
|
module_name: The name of the module to search for subclasses in.
|
|
149
149
|
classes: Class(es) we want to find the subclasses of.
|
|
150
|
-
exclude:
|
|
150
|
+
exclude: Classes we want to exclude from the returned list.
|
|
151
151
|
|
|
152
152
|
Returns:
|
|
153
153
|
The target subclasses.
|