sqlglotc 30.16.0__tar.gz → 30.18.0__tar.gz
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.
- {sqlglotc-30.16.0/sqlglotc.egg-info → sqlglotc-30.18.0}/PKG-INFO +3 -3
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/pyproject.toml +2 -2
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/executor/table.py +1 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/builders.py +9 -4
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/constraints.py +4 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/core.py +10 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/ddl.py +1 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/json.py +4 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/query.py +24 -2
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/string.py +2 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generator.py +70 -8
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/clickhouse.py +2 -6
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/dremio.py +3 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/duckdb.py +7 -9
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/hive.py +1 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/oracle.py +2 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/postgres.py +8 -4
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/presto.py +1 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/python.py +24 -3
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/snowflake.py +4 -35
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/sqlite.py +18 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/teradata.py +2 -3
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/trino.py +27 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/tsql.py +11 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/annotate_types.py +43 -14
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/canonicalize_internal_names.py +6 -4
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/normalize_identifiers.py +33 -11
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/qualify_columns.py +163 -27
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/qualify_tables.py +24 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/resolver.py +2 -3
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/scope.py +66 -9
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/simplify.py +17 -4
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parser.py +144 -59
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/bigquery.py +2 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/clickhouse.py +29 -8
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/dremio.py +6 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/mysql.py +7 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/oracle.py +1 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/postgres.py +7 -2
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/prql.py +1 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/snowflake.py +11 -35
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/spark2.py +20 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/sqlite.py +27 -3
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/starrocks.py +3 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/teradata.py +1 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/trino.py +45 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/tsql.py +3 -1
- {sqlglotc-30.16.0 → sqlglotc-30.18.0/sqlglotc.egg-info}/PKG-INFO +3 -3
- sqlglotc-30.18.0/sqlglotc.egg-info/requires.txt +6 -0
- sqlglotc-30.18.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.16.0/sqlglotc.egg-info/requires.txt +0 -6
- sqlglotc-30.16.0/sqlglotc.egg-info/scm_version.json +0 -8
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/MANIFEST.in +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/setup.cfg +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/setup.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/anonymize.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/aggregate.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/array.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/datatypes.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/properties.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/expressions/temporal.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/bigquery.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/databricks.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/dax.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/exasol.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/mysql.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/singlestore.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/spark.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/spark2.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/starrocks.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/lineage.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/databricks.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/dax.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/drill.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/druid.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/duckdb.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/exasol.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/hive.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/presto.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/spark.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/schema.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/tokenizer_core.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
- {sqlglotc-30.16.0 → sqlglotc-30.18.0}/sqlglotc.egg-info/top_level.txt +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglotc
|
|
3
|
-
Version: 30.
|
|
3
|
+
Version: 30.18.0
|
|
4
4
|
Summary: mypyc-compiled extensions for sqlglot
|
|
5
5
|
Author-email: Toby Mao <toby.mao@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://sqlglot.com/
|
|
8
8
|
Project-URL: Repository, https://github.com/tobymao/sqlglot
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
|
-
Requires-Dist: sqlglot==30.
|
|
10
|
+
Requires-Dist: sqlglot==30.18.0
|
|
11
11
|
Provides-Extra: dev
|
|
12
12
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
13
13
|
Requires-Dist: setuptools_scm; extra == "dev"
|
|
14
|
-
Requires-Dist: sqlglot-mypy>=2.
|
|
14
|
+
Requires-Dist: sqlglot-mypy>=2.3.0.post2; extra == "dev"
|
|
15
15
|
Dynamic: requires-dist
|
|
@@ -7,7 +7,7 @@ license = "MIT"
|
|
|
7
7
|
requires-python = ">= 3.10"
|
|
8
8
|
|
|
9
9
|
[project.optional-dependencies]
|
|
10
|
-
dev = ["setuptools >= 61.0", "setuptools_scm", "sqlglot-mypy >= 2.
|
|
10
|
+
dev = ["setuptools >= 61.0", "setuptools_scm", "sqlglot-mypy >= 2.3.0.post2"]
|
|
11
11
|
|
|
12
12
|
[project.urls]
|
|
13
13
|
Homepage = "https://sqlglot.com/"
|
|
@@ -17,7 +17,7 @@ Repository = "https://github.com/tobymao/sqlglot"
|
|
|
17
17
|
requires = [
|
|
18
18
|
"setuptools >= 61.0",
|
|
19
19
|
"setuptools_scm",
|
|
20
|
-
"sqlglot-mypy >= 2.
|
|
20
|
+
"sqlglot-mypy >= 2.3.0.post2",
|
|
21
21
|
"types-python-dateutil",
|
|
22
22
|
"sqlglot",
|
|
23
23
|
]
|
|
@@ -495,11 +495,16 @@ def cast(
|
|
|
495
495
|
target_dialect = Dialect.get_or_raise(dialect)
|
|
496
496
|
type_mapping = target_dialect.generator_class.TYPE_MAPPING
|
|
497
497
|
|
|
498
|
-
existing_cast_type
|
|
498
|
+
existing_cast_type = expr.to.this
|
|
499
499
|
new_cast_type: DType = data_type.this
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
500
|
+
# `this` is only a plain type enum for simple types; complex ones such as
|
|
501
|
+
# INTERVAL nest another expression there, so the equivalence check is skipped.
|
|
502
|
+
types_are_equivalent = (
|
|
503
|
+
isinstance(existing_cast_type, DType)
|
|
504
|
+
and isinstance(new_cast_type, DType)
|
|
505
|
+
and type_mapping.get(existing_cast_type, existing_cast_type.value)
|
|
506
|
+
== type_mapping.get(new_cast_type, new_cast_type.value)
|
|
507
|
+
)
|
|
503
508
|
|
|
504
509
|
if expr.is_type(data_type) or types_are_equivalent:
|
|
505
510
|
return expr
|
|
@@ -41,6 +41,10 @@ class ZeroFillColumnConstraint(ColumnConstraint):
|
|
|
41
41
|
arg_types = {}
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
class BinaryColumnConstraint(ColumnConstraint):
|
|
45
|
+
arg_types = {}
|
|
46
|
+
|
|
47
|
+
|
|
44
48
|
class PeriodForSystemTimeConstraint(Expression, ColumnConstraintKind):
|
|
45
49
|
arg_types = {"this": True, "expression": True}
|
|
46
50
|
|
|
@@ -1699,7 +1699,16 @@ class AggFunc(Func):
|
|
|
1699
1699
|
|
|
1700
1700
|
|
|
1701
1701
|
class Column(Expression, Condition):
|
|
1702
|
-
|
|
1702
|
+
# "shadow" marks a column whose qualifier is shadowed by a projection alias, so it must be
|
|
1703
|
+
# rendered unqualified in dialects where PROJECTION_ALIASES_SHADOW_SOURCE_NAMES is set
|
|
1704
|
+
arg_types = {
|
|
1705
|
+
"this": True,
|
|
1706
|
+
"table": False,
|
|
1707
|
+
"db": False,
|
|
1708
|
+
"catalog": False,
|
|
1709
|
+
"join_mark": False,
|
|
1710
|
+
"shadow": False,
|
|
1711
|
+
}
|
|
1703
1712
|
|
|
1704
1713
|
@property
|
|
1705
1714
|
def table(self) -> str:
|
|
@@ -624,6 +624,7 @@ class Group(Expression):
|
|
|
624
624
|
arg_types = {
|
|
625
625
|
"expressions": False,
|
|
626
626
|
"grouping_sets": False,
|
|
627
|
+
"grouping_sets_as_group_by_element": False,
|
|
627
628
|
"cube": False,
|
|
628
629
|
"rollup": False,
|
|
629
630
|
"totals": False,
|
|
@@ -1050,6 +1051,11 @@ class SetOperation(Expression, Query):
|
|
|
1050
1051
|
def named_selects(self) -> list[str]:
|
|
1051
1052
|
expr: Expr = self
|
|
1052
1053
|
while isinstance(expr, SetOperation):
|
|
1054
|
+
if expr.args.get("by_name"):
|
|
1055
|
+
left = t.cast(Selectable, expr.this.unnest()).named_selects
|
|
1056
|
+
right = t.cast(Selectable, expr.expression.unnest()).named_selects
|
|
1057
|
+
return list(dict.fromkeys(left + right))
|
|
1058
|
+
|
|
1053
1059
|
expr = expr.this.unnest()
|
|
1054
1060
|
return _named_selects(expr)
|
|
1055
1061
|
|
|
@@ -1886,7 +1892,7 @@ class Where(Expression):
|
|
|
1886
1892
|
class Analyze(Expression):
|
|
1887
1893
|
arg_types = {
|
|
1888
1894
|
"kind": False,
|
|
1889
|
-
"
|
|
1895
|
+
"tables": False,
|
|
1890
1896
|
"options": False,
|
|
1891
1897
|
"mode": False,
|
|
1892
1898
|
"partition": False,
|
|
@@ -2124,7 +2130,23 @@ class CaseStatement(Expression):
|
|
|
2124
2130
|
|
|
2125
2131
|
|
|
2126
2132
|
class WhileBlock(Expression):
|
|
2127
|
-
arg_types = {"this": True, "body": True}
|
|
2133
|
+
arg_types = {"this": True, "body": True, "label": False}
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
class LoopBlock(Expression):
|
|
2137
|
+
arg_types = {"body": True, "label": False}
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
class RepeatBlock(Expression):
|
|
2141
|
+
arg_types = {"body": True, "until": True, "label": False}
|
|
2142
|
+
|
|
2143
|
+
|
|
2144
|
+
class Leave(Expression):
|
|
2145
|
+
pass
|
|
2146
|
+
|
|
2147
|
+
|
|
2148
|
+
class Iterate(Expression):
|
|
2149
|
+
pass
|
|
2128
2150
|
|
|
2129
2151
|
|
|
2130
2152
|
class EndStatement(Expression):
|
|
@@ -478,7 +478,8 @@ class RegexpReplace(Expression, Func):
|
|
|
478
478
|
|
|
479
479
|
|
|
480
480
|
class RegexpSplit(Expression, Func):
|
|
481
|
-
|
|
481
|
+
# "mode" is Dremio-specific, appended after "limit" for from_arg_list compat
|
|
482
|
+
arg_types = {"this": True, "expression": True, "limit": False, "mode": False}
|
|
482
483
|
|
|
483
484
|
|
|
484
485
|
class RegexpSubstr(Expression, Func):
|
|
@@ -147,6 +147,7 @@ class Generator:
|
|
|
147
147
|
exp.AssumeColumnConstraint: lambda self, e: f"ASSUME ({self.sql(e, 'this')})",
|
|
148
148
|
exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
|
|
149
149
|
exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
|
|
150
|
+
exp.BinaryColumnConstraint: lambda *_: "BINARY",
|
|
150
151
|
exp.CaseSpecificColumnConstraint: lambda _, e: (
|
|
151
152
|
f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC"
|
|
152
153
|
),
|
|
@@ -206,6 +207,7 @@ class Generator:
|
|
|
206
207
|
exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DType.BIGINT)),
|
|
207
208
|
exp.JSONBContainsAnyTopKeys: lambda self, e: self.binary(e, "?|"),
|
|
208
209
|
exp.JSONBContainsAllTopKeys: lambda self, e: self.binary(e, "?&"),
|
|
210
|
+
exp.JSONBContainsTopKey: lambda self, e: self.binary(e, "?"),
|
|
209
211
|
exp.JSONBDeleteAtPath: lambda self, e: self.binary(e, "#-"),
|
|
210
212
|
exp.JSONBPathExists: lambda self, e: self.binary(e, "@?"),
|
|
211
213
|
exp.JSONObject: lambda self, e: self._jsonobject_sql(e),
|
|
@@ -351,6 +353,9 @@ class Generator:
|
|
|
351
353
|
# The separator for grouping sets and rollups
|
|
352
354
|
GROUPINGS_SEP = ","
|
|
353
355
|
|
|
356
|
+
# Whether GROUPING SETS can follow GROUP BY expressions without a comma
|
|
357
|
+
SUPPORTS_GROUPING_SETS_AS_SUFFIX = False
|
|
358
|
+
|
|
354
359
|
# The string used for creating an index on a table
|
|
355
360
|
INDEX_ON = "ON"
|
|
356
361
|
|
|
@@ -850,6 +855,16 @@ class Generator:
|
|
|
850
855
|
|
|
851
856
|
RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: t.ClassVar[tuple[type[exp.Expr], ...]] = ()
|
|
852
857
|
|
|
858
|
+
MOD_OPERATOR = "%"
|
|
859
|
+
|
|
860
|
+
# Infix operators that bind at least as tightly as %, so a Mod on their right side needs parentheses
|
|
861
|
+
MOD_PAREN_PARENT_TYPES: t.ClassVar[tuple[type[exp.Expr], ...]] = (
|
|
862
|
+
exp.Mul,
|
|
863
|
+
exp.Div,
|
|
864
|
+
exp.IntDiv,
|
|
865
|
+
exp.Mod,
|
|
866
|
+
)
|
|
867
|
+
|
|
853
868
|
SAFE_JSON_PATH_KEY_RE: t.ClassVar = exp.SAFE_IDENTIFIER_RE
|
|
854
869
|
|
|
855
870
|
SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
|
|
@@ -1151,6 +1166,10 @@ class Generator:
|
|
|
1151
1166
|
return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
|
|
1152
1167
|
|
|
1153
1168
|
def column_parts(self, expression: exp.Column) -> str:
|
|
1169
|
+
if expression.args.get("shadow") and self.dialect.PROJECTION_ALIASES_SHADOW_SOURCE_NAMES:
|
|
1170
|
+
# The qualifier would be captured by a colliding projection alias (see qualify_columns)
|
|
1171
|
+
return self.sql(expression, "this")
|
|
1172
|
+
|
|
1154
1173
|
return ".".join(
|
|
1155
1174
|
self.sql(part)
|
|
1156
1175
|
for part in (
|
|
@@ -1822,7 +1841,7 @@ class Generator:
|
|
|
1822
1841
|
return self.prepend_ctes(expression, f"DELETE{hint}{tables}{expression_sql}")
|
|
1823
1842
|
|
|
1824
1843
|
def drop_sql(self, expression: exp.Drop) -> str:
|
|
1825
|
-
|
|
1844
|
+
tables = self.expressions(expression, key="tables", flat=True)
|
|
1826
1845
|
expressions = self.expressions(expression, flat=True)
|
|
1827
1846
|
expressions = f" ({expressions})" if expressions else ""
|
|
1828
1847
|
kind = expression.args["kind"]
|
|
@@ -1844,7 +1863,7 @@ class Generator:
|
|
|
1844
1863
|
purge = " PURGE" if expression.args.get("purge") else ""
|
|
1845
1864
|
sync = " SYNC" if expression.args.get("sync") else ""
|
|
1846
1865
|
force = " FORCE" if expression.args.get("force") else ""
|
|
1847
|
-
return f"DROP{temporary}{materialized}{iceberg} {kind}{concurrently_sql}{exists_sql}{
|
|
1866
|
+
return f"DROP{temporary}{materialized}{iceberg} {kind}{concurrently_sql}{exists_sql}{tables}{on_cluster}{expressions}{cascade}{restrict}{constraints}{purge}{sync}{force}"
|
|
1848
1867
|
|
|
1849
1868
|
def set_operation(self, expression: exp.SetOperation) -> str:
|
|
1850
1869
|
op_type = type(expression)
|
|
@@ -2815,7 +2834,18 @@ class Generator:
|
|
|
2815
2834
|
and groupings
|
|
2816
2835
|
and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
|
|
2817
2836
|
):
|
|
2818
|
-
|
|
2837
|
+
add_separator = True
|
|
2838
|
+
|
|
2839
|
+
if grouping_sets and not expression.args.get("grouping_sets_as_group_by_element"):
|
|
2840
|
+
if self.SUPPORTS_GROUPING_SETS_AS_SUFFIX:
|
|
2841
|
+
add_separator = False
|
|
2842
|
+
else:
|
|
2843
|
+
self.unsupported(
|
|
2844
|
+
"GROUPING SETS without a comma after GROUP BY expressions is not supported"
|
|
2845
|
+
)
|
|
2846
|
+
|
|
2847
|
+
if add_separator:
|
|
2848
|
+
group_by = f"{group_by}{self.GROUPINGS_SEP}"
|
|
2819
2849
|
|
|
2820
2850
|
return f"{group_by}{groupings}"
|
|
2821
2851
|
|
|
@@ -4583,7 +4613,15 @@ class Generator:
|
|
|
4583
4613
|
return self.binary(expression, "<=")
|
|
4584
4614
|
|
|
4585
4615
|
def mod_sql(self, expression: exp.Mod) -> str:
|
|
4586
|
-
|
|
4616
|
+
this = self.sql(expression, "this")
|
|
4617
|
+
expr = self.sql(expression, "expression")
|
|
4618
|
+
sql = f"{this} {self.maybe_comment(self.MOD_OPERATOR, comments=expression.comments)} {expr}"
|
|
4619
|
+
|
|
4620
|
+
parent = expression.parent
|
|
4621
|
+
if isinstance(parent, self.MOD_PAREN_PARENT_TYPES) and parent.expression is expression:
|
|
4622
|
+
return f"({sql})"
|
|
4623
|
+
|
|
4624
|
+
return sql
|
|
4587
4625
|
|
|
4588
4626
|
def mul_sql(self, expression: exp.Mul) -> str:
|
|
4589
4627
|
return self.binary(expression, "*")
|
|
@@ -5031,6 +5069,12 @@ class Generator:
|
|
|
5031
5069
|
|
|
5032
5070
|
return self.sql(case)
|
|
5033
5071
|
|
|
5072
|
+
def nthvalue_sql(self, expression: exp.NthValue) -> str:
|
|
5073
|
+
if expression.args.get("from_first") is False:
|
|
5074
|
+
self.unsupported("NTH_VALUE FROM LAST is not supported")
|
|
5075
|
+
|
|
5076
|
+
return self.function_fallback_sql(expression)
|
|
5077
|
+
|
|
5034
5078
|
def comprehension_sql(self, expression: exp.Comprehension) -> str:
|
|
5035
5079
|
this = self.sql(expression, "this")
|
|
5036
5080
|
expr = self.sql(expression, "expression")
|
|
@@ -5391,9 +5435,11 @@ class Generator:
|
|
|
5391
5435
|
|
|
5392
5436
|
if self.IGNORE_NULLS_IN_FUNC and not expression.meta_get("inline"):
|
|
5393
5437
|
if self.IGNORE_NULLS_BEFORE_ORDER:
|
|
5438
|
+
from sqlglot.optimizer.scope import find_all_in_scope
|
|
5439
|
+
|
|
5394
5440
|
# The first modifier here will be the one closest to the AggFunc's arg
|
|
5395
5441
|
mods = sorted(
|
|
5396
|
-
expression
|
|
5442
|
+
find_all_in_scope(expression, exp.HavingMax, exp.Order, exp.Limit),
|
|
5397
5443
|
key=lambda x: (
|
|
5398
5444
|
0
|
|
5399
5445
|
if isinstance(x, exp.HavingMax)
|
|
@@ -6033,8 +6079,8 @@ class Generator:
|
|
|
6033
6079
|
options = f" {options}" if options else ""
|
|
6034
6080
|
kind = self.sql(expression, "kind")
|
|
6035
6081
|
kind = f" {kind}" if kind else ""
|
|
6036
|
-
|
|
6037
|
-
|
|
6082
|
+
tables = self.expressions(expression, key="tables", flat=True)
|
|
6083
|
+
tables = f" {tables}" if tables else ""
|
|
6038
6084
|
mode = self.sql(expression, "mode")
|
|
6039
6085
|
mode = f" {mode}" if mode else ""
|
|
6040
6086
|
properties = self.sql(expression, "properties")
|
|
@@ -6043,7 +6089,7 @@ class Generator:
|
|
|
6043
6089
|
partition = f" {partition}" if partition else ""
|
|
6044
6090
|
inner_expression = self.sql(expression, "expression")
|
|
6045
6091
|
inner_expression = f" {inner_expression}" if inner_expression else ""
|
|
6046
|
-
return f"ANALYZE{options}{kind}{
|
|
6092
|
+
return f"ANALYZE{options}{kind}{tables}{partition}{mode}{inner_expression}{properties}"
|
|
6047
6093
|
|
|
6048
6094
|
def xmltable_sql(self, expression: exp.XMLTable) -> str:
|
|
6049
6095
|
this = self.sql(expression, "this")
|
|
@@ -6319,6 +6365,22 @@ class Generator:
|
|
|
6319
6365
|
self.unsupported("Unsupported While block syntax")
|
|
6320
6366
|
return ""
|
|
6321
6367
|
|
|
6368
|
+
def loopblock_sql(self, expression: exp.LoopBlock) -> str:
|
|
6369
|
+
self.unsupported("Unsupported Loop block syntax")
|
|
6370
|
+
return ""
|
|
6371
|
+
|
|
6372
|
+
def repeatblock_sql(self, expression: exp.RepeatBlock) -> str:
|
|
6373
|
+
self.unsupported("Unsupported Repeat block syntax")
|
|
6374
|
+
return ""
|
|
6375
|
+
|
|
6376
|
+
def leave_sql(self, expression: exp.Leave) -> str:
|
|
6377
|
+
self.unsupported("Unsupported Leave syntax")
|
|
6378
|
+
return ""
|
|
6379
|
+
|
|
6380
|
+
def iterate_sql(self, expression: exp.Iterate) -> str:
|
|
6381
|
+
self.unsupported("Unsupported Iterate syntax")
|
|
6382
|
+
return ""
|
|
6383
|
+
|
|
6322
6384
|
def execute_sql(self, expression: exp.Execute) -> str:
|
|
6323
6385
|
self.unsupported("Unsupported Execute syntax")
|
|
6324
6386
|
return ""
|
|
@@ -372,12 +372,8 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
372
372
|
exp.SchemaCommentProperty: lambda self, e: self.naked_property(e),
|
|
373
373
|
exp.Stddev: rename_func("stddevSamp"),
|
|
374
374
|
exp.Chr: rename_func("CHAR"),
|
|
375
|
-
exp.Lag:
|
|
376
|
-
|
|
377
|
-
),
|
|
378
|
-
exp.Lead: lambda self, e: self.func(
|
|
379
|
-
"leadInFrame", e.this, e.args.get("offset"), e.args.get("default")
|
|
380
|
-
),
|
|
375
|
+
exp.Lag: rename_func("lag"),
|
|
376
|
+
exp.Lead: rename_func("lead"),
|
|
381
377
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
|
382
378
|
rename_func("editDistance")
|
|
383
379
|
),
|
|
@@ -70,6 +70,9 @@ class DremioGenerator(generator.Generator):
|
|
|
70
70
|
exp.DateAdd: _date_delta_sql("DATE_ADD"),
|
|
71
71
|
exp.DateSub: _date_delta_sql("DATE_SUB"),
|
|
72
72
|
exp.GenerateSeries: rename_func("ARRAY_GENERATE_RANGE"),
|
|
73
|
+
exp.RegexpSplit: lambda self, e: self.func(
|
|
74
|
+
"REGEXP_SPLIT", e.this, e.expression, e.args.get("mode"), e.args.get("limit")
|
|
75
|
+
),
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
def version_sql(self, expression: exp.Version) -> str:
|
|
@@ -10,10 +10,10 @@ from sqlglot.dialects.dialect import (
|
|
|
10
10
|
DATETIME_DELTA,
|
|
11
11
|
JSON_EXTRACT_TYPE,
|
|
12
12
|
approx_count_distinct_sql,
|
|
13
|
+
arrow_json_extract_sql,
|
|
13
14
|
array_append_sql,
|
|
14
15
|
array_compact_sql,
|
|
15
16
|
array_concat_sql,
|
|
16
|
-
arrow_json_extract_sql,
|
|
17
17
|
count_if_to_sum,
|
|
18
18
|
date_delta_to_binary_interval_op,
|
|
19
19
|
datestrtodate_sql,
|
|
@@ -2469,13 +2469,6 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2469
2469
|
|
|
2470
2470
|
return self.sql(result)
|
|
2471
2471
|
|
|
2472
|
-
def nthvalue_sql(self, expression: exp.NthValue) -> str:
|
|
2473
|
-
from_first = expression.args.get("from_first", True)
|
|
2474
|
-
if not from_first:
|
|
2475
|
-
self.unsupported("DuckDB's NTH_VALUE doesn't support starting from the end ")
|
|
2476
|
-
|
|
2477
|
-
return self.function_fallback_sql(expression)
|
|
2478
|
-
|
|
2479
2472
|
def randstr_sql(self, expression: exp.Randstr) -> str:
|
|
2480
2473
|
"""
|
|
2481
2474
|
Transpile Snowflake's RANDSTR to DuckDB equivalent using deterministic hash-based random.
|
|
@@ -4720,9 +4713,14 @@ class DuckDBGenerator(generator.Generator):
|
|
|
4720
4713
|
|
|
4721
4714
|
def jsonextractscalar_sql(self, expression: exp.JSONExtractScalar) -> str:
|
|
4722
4715
|
if expression.args.get("scalar_only"):
|
|
4723
|
-
|
|
4716
|
+
json_value = exp.JSONExtractScalar(
|
|
4724
4717
|
this=rename_func("JSON_VALUE")(self, expression), expression="'$'"
|
|
4725
4718
|
)
|
|
4719
|
+
|
|
4720
|
+
# `->>` binds looser than most operators, so the wrap logic needs the parent
|
|
4721
|
+
json_value.parent = expression.parent
|
|
4722
|
+
expression = json_value
|
|
4723
|
+
|
|
4726
4724
|
return _arrow_json_extract_sql(self, expression)
|
|
4727
4725
|
|
|
4728
4726
|
def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
|
|
@@ -223,6 +223,7 @@ class HiveGenerator(generator.Generator):
|
|
|
223
223
|
SELECT_KINDS: tuple[str, ...] = ()
|
|
224
224
|
TRY_SUPPORTED = False
|
|
225
225
|
SUPPORTS_UESCAPE = False
|
|
226
|
+
SUPPORTS_GROUPING_SETS_AS_SUFFIX = True
|
|
226
227
|
SUPPORTS_DECODE_CASE = False
|
|
227
228
|
LIMIT_FETCH = "LIMIT"
|
|
228
229
|
TABLESAMPLE_WITH_METHOD = False
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
from sqlglot import exp, generator, transforms
|
|
5
4
|
from sqlglot.dialects.dialect import (
|
|
6
5
|
groupconcat_sql,
|
|
7
6
|
no_ilike_sql,
|
|
7
|
+
nth_value_from_sql,
|
|
8
8
|
rename_func,
|
|
9
9
|
strposition_sql,
|
|
10
10
|
trim_sql,
|
|
@@ -77,6 +77,7 @@ class OracleGenerator(generator.Generator):
|
|
|
77
77
|
exp.LogicalOr: rename_func("MAX"),
|
|
78
78
|
exp.LogicalAnd: rename_func("MIN"),
|
|
79
79
|
exp.Mod: rename_func("MOD"),
|
|
80
|
+
exp.NthValue: nth_value_from_sql,
|
|
80
81
|
exp.Rand: rename_func("DBMS_RANDOM.VALUE"),
|
|
81
82
|
exp.Select: transforms.preprocess(
|
|
82
83
|
[
|
|
@@ -7,6 +7,7 @@ from sqlglot.dialects.dialect import (
|
|
|
7
7
|
DATE_ADD_OR_SUB,
|
|
8
8
|
JSON_EXTRACT_TYPE,
|
|
9
9
|
any_value_to_max_sql,
|
|
10
|
+
arrow_json_extract_sql,
|
|
10
11
|
array_append_sql,
|
|
11
12
|
array_concat_sql,
|
|
12
13
|
bool_xor_sql,
|
|
@@ -189,7 +190,11 @@ def _json_extract_sql(
|
|
|
189
190
|
if not isinstance(path, (exp.JSONPath, exp.Variadic)) and not ensure_list(
|
|
190
191
|
expression.args.get("expressions")
|
|
191
192
|
):
|
|
192
|
-
return self
|
|
193
|
+
return arrow_json_extract_sql(self, expression, op=op)
|
|
194
|
+
|
|
195
|
+
# JSON_EXTRACT_PATH requires a key, so use an empty variadic array for the root path
|
|
196
|
+
if len(path.expressions) == 1 and isinstance(path.expressions[0], exp.JSONPathRoot):
|
|
197
|
+
expression.set("expression", exp.Variadic(this=exp.Literal.string("{}")))
|
|
193
198
|
|
|
194
199
|
if expression.args.get("only_json_types"):
|
|
195
200
|
return json_extract_segments(name, quoted_index=False, op=op)(self, expression)
|
|
@@ -347,9 +352,8 @@ class PostgresGenerator(generator.Generator):
|
|
|
347
352
|
),
|
|
348
353
|
exp.JSONExtract: _json_extract_sql("JSON_EXTRACT_PATH", "->"),
|
|
349
354
|
exp.JSONExtractScalar: _json_extract_sql("JSON_EXTRACT_PATH_TEXT", "->>"),
|
|
350
|
-
exp.JSONBExtract: lambda self, e: self
|
|
351
|
-
exp.JSONBExtractScalar: lambda self, e: self
|
|
352
|
-
exp.JSONBContains: lambda self, e: self.binary(e, "?"),
|
|
355
|
+
exp.JSONBExtract: lambda self, e: arrow_json_extract_sql(self, e, op="#>"),
|
|
356
|
+
exp.JSONBExtractScalar: lambda self, e: arrow_json_extract_sql(self, e, op="#>>"),
|
|
353
357
|
exp.ParseJSON: lambda self, e: self.sql(exp.cast(e.this, exp.DType.JSON)),
|
|
354
358
|
exp.JSONPathKey: json_path_key_only_name,
|
|
355
359
|
exp.JSONPathRoot: lambda *_: "",
|
|
@@ -383,7 +383,7 @@ class PrestoGenerator(generator.Generator):
|
|
|
383
383
|
transforms.eliminate_window_clause,
|
|
384
384
|
transforms.eliminate_qualify,
|
|
385
385
|
transforms.eliminate_distinct_on,
|
|
386
|
-
transforms.explode_projection_to_unnest(1),
|
|
386
|
+
transforms.explode_projection_to_unnest(1, unnest_map=True),
|
|
387
387
|
transforms.eliminate_semi_and_anti_joins,
|
|
388
388
|
amend_exploded_column_table,
|
|
389
389
|
]
|
|
@@ -43,7 +43,7 @@ def _case_sql(self, expression):
|
|
|
43
43
|
condition = f"{this} = ({condition})" if this else condition
|
|
44
44
|
chain = f"{true} if {condition} else ({chain})"
|
|
45
45
|
|
|
46
|
-
return chain
|
|
46
|
+
return f"({chain})"
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def _lambda_sql(self, e: exp.Lambda) -> str:
|
|
@@ -58,6 +58,15 @@ def _lambda_sql(self, e: exp.Lambda) -> str:
|
|
|
58
58
|
return f"lambda {self.expressions(e, flat=True)}: {self.sql(e, 'this')}"
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
def _like_sql(self: generator.Generator, e: exp.Like | exp.ILike) -> str:
|
|
62
|
+
sql = self.func(e.key, e.this, e.expression)
|
|
63
|
+
|
|
64
|
+
if e.args.get("negate"):
|
|
65
|
+
sql = f"NOT({sql})"
|
|
66
|
+
|
|
67
|
+
return sql
|
|
68
|
+
|
|
69
|
+
|
|
61
70
|
def _div_sql(self: generator.Generator, e: exp.Div) -> str:
|
|
62
71
|
denominator = self.sql(e, "expression")
|
|
63
72
|
|
|
@@ -66,12 +75,21 @@ def _div_sql(self: generator.Generator, e: exp.Div) -> str:
|
|
|
66
75
|
|
|
67
76
|
sql = f"DIV({self.sql(e, 'this')}, {denominator})"
|
|
68
77
|
|
|
69
|
-
if e.args.get("typed")
|
|
70
|
-
|
|
78
|
+
if e.args.get("typed") and not (
|
|
79
|
+
e.this.is_type(*exp.DataType.REAL_TYPES) or e.expression.is_type(*exp.DataType.REAL_TYPES)
|
|
80
|
+
):
|
|
81
|
+
sql = f"INT({sql})"
|
|
71
82
|
|
|
72
83
|
return sql
|
|
73
84
|
|
|
74
85
|
|
|
86
|
+
def _dpipe_sql(self: generator.Generator, e: exp.DPipe) -> str:
|
|
87
|
+
if e.this.is_type(exp.DataType.Type.ARRAY) or e.expression.is_type(exp.DataType.Type.ARRAY):
|
|
88
|
+
return self.func("ARRAYCONCAT", e.this, e.expression)
|
|
89
|
+
|
|
90
|
+
return self.func("SAFECONCAT" if e.args.get("safe") else "CONCAT", e.this, e.expression)
|
|
91
|
+
|
|
92
|
+
|
|
75
93
|
class PythonGenerator(generator.Generator):
|
|
76
94
|
TRANSFORMS = {
|
|
77
95
|
**{klass: _rename for klass in subclasses(exp.__name__, exp.Binary)},
|
|
@@ -89,7 +107,9 @@ class PythonGenerator(generator.Generator):
|
|
|
89
107
|
),
|
|
90
108
|
exp.Distinct: lambda self, e: f"set({self.sql(e, 'this')})",
|
|
91
109
|
exp.Div: _div_sql,
|
|
110
|
+
exp.DPipe: _dpipe_sql,
|
|
92
111
|
exp.Extract: lambda self, e: f"EXTRACT('{e.name.lower()}', {self.sql(e, 'expression')})",
|
|
112
|
+
exp.ILike: _like_sql,
|
|
93
113
|
exp.In: lambda self, e: self.func("IN", e.this, *e.expressions),
|
|
94
114
|
exp.Interval: lambda self, e: f"INTERVAL({self.sql(e.this)}, '{self.sql(e.unit)}')",
|
|
95
115
|
exp.Is: lambda self, e: (
|
|
@@ -102,6 +122,7 @@ class PythonGenerator(generator.Generator):
|
|
|
102
122
|
exp.JSONPathKey: lambda self, e: f"'{self.sql(e.this)}'",
|
|
103
123
|
exp.JSONPathSubscript: lambda self, e: f"'{e.this}'",
|
|
104
124
|
exp.Lambda: _lambda_sql,
|
|
125
|
+
exp.Like: _like_sql,
|
|
105
126
|
exp.Not: lambda self, e: self.func("NOT", e.this),
|
|
106
127
|
exp.Null: lambda *_: "None",
|
|
107
128
|
exp.Or: lambda self, e: f"OR(lambda: {self.sql(e.left)}, lambda: {self.sql(e.right)})",
|
|
@@ -17,6 +17,7 @@ from sqlglot.dialects.dialect import (
|
|
|
17
17
|
min_or_least,
|
|
18
18
|
no_make_interval_sql,
|
|
19
19
|
no_timestamp_sql,
|
|
20
|
+
nth_value_from_sql,
|
|
20
21
|
rename_func,
|
|
21
22
|
strposition_sql,
|
|
22
23
|
timestampdiff_sql,
|
|
@@ -96,25 +97,6 @@ def _unqualify_pivot_columns(expression: exp.Expr) -> exp.Expr:
|
|
|
96
97
|
return expression
|
|
97
98
|
|
|
98
99
|
|
|
99
|
-
def _flatten_structured_types_unless_iceberg(expression: exp.Expr) -> exp.Expr:
|
|
100
|
-
assert isinstance(expression, exp.Create)
|
|
101
|
-
|
|
102
|
-
def _flatten_structured_type(expression: exp.Expr) -> exp.Expr:
|
|
103
|
-
if isinstance(expression, exp.DataType) and expression.this in exp.DataType.NESTED_TYPES:
|
|
104
|
-
expression.set("expressions", None)
|
|
105
|
-
return expression
|
|
106
|
-
|
|
107
|
-
props = expression.args.get("properties")
|
|
108
|
-
if isinstance(expression.this, exp.Schema) and not (props and props.find(exp.IcebergProperty)):
|
|
109
|
-
for schema_expression in expression.this.expressions:
|
|
110
|
-
if isinstance(schema_expression, exp.ColumnDef):
|
|
111
|
-
column_type = schema_expression.kind
|
|
112
|
-
if isinstance(column_type, exp.DataType):
|
|
113
|
-
column_type.transform(_flatten_structured_type, copy=False)
|
|
114
|
-
|
|
115
|
-
return expression
|
|
116
|
-
|
|
117
|
-
|
|
118
100
|
def _unnest_generate_date_array(unnest: exp.Unnest) -> None:
|
|
119
101
|
generate_date_array = unnest.expressions[0]
|
|
120
102
|
start = generate_date_array.args.get("start")
|
|
@@ -437,7 +419,6 @@ class SnowflakeGenerator(generator.Generator):
|
|
|
437
419
|
exp.BitwiseNot: rename_func("BITNOT"),
|
|
438
420
|
exp.BitwiseLeftShift: rename_func("BITSHIFTLEFT"),
|
|
439
421
|
exp.BitwiseRightShift: rename_func("BITSHIFTRIGHT"),
|
|
440
|
-
exp.Create: transforms.preprocess([_flatten_structured_types_unless_iceberg]),
|
|
441
422
|
exp.CurrentTimestamp: lambda self, e: (
|
|
442
423
|
self.func("SYSDATE") if e.args.get("sysdate") else self.function_fallback_sql(e)
|
|
443
424
|
),
|
|
@@ -518,6 +499,7 @@ class SnowflakeGenerator(generator.Generator):
|
|
|
518
499
|
exp.MakeInterval: no_make_interval_sql,
|
|
519
500
|
exp.Max: max_or_greatest,
|
|
520
501
|
exp.Min: min_or_least,
|
|
502
|
+
exp.NthValue: nth_value_from_sql,
|
|
521
503
|
exp.ParseJSON: lambda self, e: self.func(
|
|
522
504
|
f"{'TRY_' if e.args.get('safe') else ''}PARSE_JSON", e.this
|
|
523
505
|
),
|
|
@@ -629,19 +611,6 @@ class SnowflakeGenerator(generator.Generator):
|
|
|
629
611
|
nulls_first = None
|
|
630
612
|
return self.func("ARRAY_SORT", expression.this, asc, nulls_first)
|
|
631
613
|
|
|
632
|
-
def nthvalue_sql(self, expression: exp.NthValue) -> str:
|
|
633
|
-
result = self.func("NTH_VALUE", expression.this, expression.args.get("offset"))
|
|
634
|
-
|
|
635
|
-
from_first = expression.args.get("from_first")
|
|
636
|
-
|
|
637
|
-
if from_first is not None:
|
|
638
|
-
if from_first:
|
|
639
|
-
result = result + " FROM FIRST"
|
|
640
|
-
else:
|
|
641
|
-
result = result + " FROM LAST"
|
|
642
|
-
|
|
643
|
-
return result
|
|
644
|
-
|
|
645
614
|
SUPPORTED_JSON_PATH_PARTS = {
|
|
646
615
|
exp.JSONPathKey,
|
|
647
616
|
exp.JSONPathRoot,
|
|
@@ -1179,7 +1148,7 @@ class SnowflakeGenerator(generator.Generator):
|
|
|
1179
1148
|
# Snowflake doesn't support FILTER (WHERE cond), so we rewrite it into an
|
|
1180
1149
|
# equivalent conditional aggregation, i.e. wrap the input values in an IFF
|
|
1181
1150
|
agg = expression.this
|
|
1182
|
-
agg_arg = agg.this
|
|
1151
|
+
agg_arg = seq_get(agg.expressions, 0) if isinstance(agg, exp.Anonymous) else agg.this
|
|
1183
1152
|
cond = expression.expression.this
|
|
1184
1153
|
|
|
1185
1154
|
if isinstance(agg, exp.WithinGroup):
|
|
@@ -1203,7 +1172,7 @@ class SnowflakeGenerator(generator.Generator):
|
|
|
1203
1172
|
# `COUNT(*/t.*) FILTER (WHERE cond)` counts qualifying rows, but a star can't be an IFF
|
|
1204
1173
|
# argument: `IFF(cond, *, NULL)` expands to multiple columns once the table has 2+ of
|
|
1205
1174
|
# them, which Snowflake rejects. Use its native COUNT_IF instead.
|
|
1206
|
-
if isinstance(agg, exp.Count) and agg_arg.is_star:
|
|
1175
|
+
if isinstance(agg, exp.Count) and isinstance(agg_arg, exp.Expression) and agg_arg.is_star:
|
|
1207
1176
|
return self.func("COUNT_IF", cond)
|
|
1208
1177
|
|
|
1209
1178
|
# `DISTINCT` and `ORDER BY` are part of the aggregate's own argument list, so the
|