sqlglotc 30.23.0__tar.gz → 30.24.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.23.0/sqlglotc.egg-info → sqlglotc-30.24.0}/PKG-INFO +2 -2
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/setup.py +17 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/array.py +1 -5
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/query.py +8 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generator.py +31 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/bigquery.py +142 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/clickhouse.py +2 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/duckdb.py +36 -40
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/fabric.py +5 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/hive.py +4 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/mysql.py +3 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/postgres.py +8 -3
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/presto.py +14 -7
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/snowflake.py +15 -7
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/spark2.py +22 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/starrocks.py +35 -4
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/teradata.py +11 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/helper.py +17 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/annotate_types.py +24 -16
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/qualify_columns.py +33 -9
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/scope.py +8 -7
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/simplify.py +23 -5
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parser.py +145 -32
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/clickhouse.py +6 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/doris.py +3 -3
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/dremio.py +3 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/drill.py +3 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/duckdb.py +34 -8
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/hive.py +13 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/mysql.py +71 -3
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/oracle.py +3 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/postgres.py +3 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/presto.py +2 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/prql.py +2 -8
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/redshift.py +3 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/singlestore.py +3 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/snowflake.py +4 -4
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/spark2.py +4 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/sqlite.py +14 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/starrocks.py +73 -10
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/teradata.py +15 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/trino.py +6 -1
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/tokenizer_core.py +1 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0/sqlglotc.egg-info}/PKG-INFO +2 -2
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglotc.egg-info/requires.txt +1 -1
- sqlglotc-30.24.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.23.0/sqlglotc.egg-info/scm_version.json +0 -8
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/MANIFEST.in +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/pyproject.toml +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/setup.cfg +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/anonymize.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/aggregate.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/builders.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/constraints.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/core.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/datatypes.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/ddl.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/json.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/properties.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/string.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/expressions/temporal.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/databricks.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/dax.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/dremio.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/exasol.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/oracle.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/python.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/singlestore.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/spark.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/sqlite.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/trino.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/generators/tsql.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/lineage.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/qualify_tables.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/optimizer/resolver.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/bigquery.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/databricks.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/dax.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/druid.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/exasol.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/spark.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/parsers/tsql.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
- {sqlglotc-30.23.0 → sqlglotc-30.24.0}/sqlglotc.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglotc
|
|
3
|
-
Version: 30.
|
|
3
|
+
Version: 30.24.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.24.0
|
|
11
11
|
Provides-Extra: dev
|
|
12
12
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
13
13
|
Requires-Dist: setuptools_scm; extra == "dev"
|
|
@@ -90,6 +90,16 @@ def _source_paths():
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
class build_ext(_build_ext):
|
|
93
|
+
def _remove_obsolete_extensions(self, directory, expected):
|
|
94
|
+
if self.dry_run:
|
|
95
|
+
return
|
|
96
|
+
expected = {os.path.abspath(path) for path in expected}
|
|
97
|
+
for root, _, files in os.walk(directory):
|
|
98
|
+
for filename in files:
|
|
99
|
+
path = os.path.join(root, filename)
|
|
100
|
+
if filename.endswith((".so", ".pyd")) and os.path.abspath(path) not in expected:
|
|
101
|
+
self.execute(os.remove, (path,), f"removing obsolete extension {path}")
|
|
102
|
+
|
|
93
103
|
def finalize_options(self):
|
|
94
104
|
super().finalize_options()
|
|
95
105
|
# Compile in parallel by default; builds driven by pip (wheel/sdist installs)
|
|
@@ -97,8 +107,13 @@ class build_ext(_build_ext):
|
|
|
97
107
|
if self.parallel is None:
|
|
98
108
|
self.parallel = os.cpu_count() or 1
|
|
99
109
|
|
|
110
|
+
def build_extensions(self):
|
|
111
|
+
self._remove_obsolete_extensions(self.build_lib, self.get_outputs())
|
|
112
|
+
super().build_extensions()
|
|
113
|
+
|
|
100
114
|
def copy_extensions_to_source(self):
|
|
101
115
|
"""For editable installs, put sqlglot.* .so files in the sqlglot source dir."""
|
|
116
|
+
expected = []
|
|
102
117
|
for ext in self.extensions:
|
|
103
118
|
fullname = self.get_ext_fullname(ext.name)
|
|
104
119
|
filename = self.get_ext_filename(fullname)
|
|
@@ -112,7 +127,9 @@ class build_ext(_build_ext):
|
|
|
112
127
|
# Place the mypyc runtime helper (e.g., HASH__mypyc) inside sqlglot/.
|
|
113
128
|
# sqlglot/__init__.py bootstraps it into sys.modules for editable installs.
|
|
114
129
|
dst = os.path.join(SQLGLOT_SRC, os.path.basename(filename))
|
|
130
|
+
expected.append(dst)
|
|
115
131
|
self.copy_file(src, dst, level=self.verbose)
|
|
132
|
+
self._remove_obsolete_extensions(SQLGLOT_SRC, expected)
|
|
116
133
|
|
|
117
134
|
|
|
118
135
|
class sdist(_sdist):
|
|
@@ -98,17 +98,13 @@ class ArraySlice(Expression, Func):
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
class ArraySort(Expression, Func):
|
|
101
|
-
arg_types = {"this": True, "expression": False}
|
|
101
|
+
arg_types = {"this": True, "expression": False, "asc": False, "nulls_first": False}
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
class Shuffle(Expression, Func):
|
|
105
105
|
pass
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
class SortArray(Expression, Func):
|
|
109
|
-
arg_types = {"this": True, "asc": False, "nulls_first": False}
|
|
110
|
-
|
|
111
|
-
|
|
112
108
|
# Array predicates / search
|
|
113
109
|
|
|
114
110
|
|
|
@@ -1938,6 +1938,10 @@ class WindowSpec(Expression):
|
|
|
1938
1938
|
}
|
|
1939
1939
|
|
|
1940
1940
|
|
|
1941
|
+
class PartitionByExcluding(Expression):
|
|
1942
|
+
arg_types = {"expressions": True}
|
|
1943
|
+
|
|
1944
|
+
|
|
1941
1945
|
class PreWhere(Expression):
|
|
1942
1946
|
pass
|
|
1943
1947
|
|
|
@@ -2020,6 +2024,10 @@ class DropPartition(Expression):
|
|
|
2020
2024
|
arg_types = {"expressions": True, "exists": False}
|
|
2021
2025
|
|
|
2022
2026
|
|
|
2027
|
+
class ReplaceColumns(Expression):
|
|
2028
|
+
arg_types = {"expressions": True}
|
|
2029
|
+
|
|
2030
|
+
|
|
2023
2031
|
class ReplacePartition(Expression):
|
|
2024
2032
|
arg_types = {"expression": True, "source": True}
|
|
2025
2033
|
|
|
@@ -2178,6 +2178,7 @@ class Generator:
|
|
|
2178
2178
|
property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
|
|
2179
2179
|
if not property_name:
|
|
2180
2180
|
self.unsupported(f"Unsupported property {expression.key}")
|
|
2181
|
+
return ""
|
|
2181
2182
|
|
|
2182
2183
|
return f"{property_name}={self.sql(expression, 'this')}"
|
|
2183
2184
|
|
|
@@ -2319,6 +2320,10 @@ class Generator:
|
|
|
2319
2320
|
override = " OVERRIDE" if expression.args.get("override") else ""
|
|
2320
2321
|
return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
|
|
2321
2322
|
|
|
2323
|
+
def lockingstatement_sql(self, expression: exp.LockingStatement) -> str:
|
|
2324
|
+
self.unsupported("LOCKING request modifier is not supported")
|
|
2325
|
+
return self.sql(expression, "expression")
|
|
2326
|
+
|
|
2322
2327
|
def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
|
|
2323
2328
|
data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
|
|
2324
2329
|
statistics = expression.args.get("statistics")
|
|
@@ -2561,6 +2566,10 @@ class Generator:
|
|
|
2561
2566
|
expression: exp.TableSample,
|
|
2562
2567
|
tablesample_keyword: str | None = None,
|
|
2563
2568
|
) -> str:
|
|
2569
|
+
if expression.expressions:
|
|
2570
|
+
self.unsupported("Multiple sample sets are not supported")
|
|
2571
|
+
return ""
|
|
2572
|
+
|
|
2564
2573
|
method = self.sql(expression, "method")
|
|
2565
2574
|
method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
|
|
2566
2575
|
numerator = self.sql(expression, "bucket_numerator")
|
|
@@ -3638,6 +3647,9 @@ class Generator:
|
|
|
3638
3647
|
partition = self.expressions(expression, key="partition_by", flat=True)
|
|
3639
3648
|
return f"PARTITION BY {partition}" if partition else ""
|
|
3640
3649
|
|
|
3650
|
+
def partitionbyexcluding_sql(self, expression: exp.PartitionByExcluding) -> str:
|
|
3651
|
+
return f"EXCLUDING {self.expressions(expression, flat=True)}"
|
|
3652
|
+
|
|
3641
3653
|
def windowspec_sql(self, expression: exp.WindowSpec) -> str:
|
|
3642
3654
|
kind = self.sql(expression, "kind")
|
|
3643
3655
|
start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
|
|
@@ -4517,6 +4529,9 @@ class Generator:
|
|
|
4517
4529
|
def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
|
|
4518
4530
|
return f"ADD {self.expressions(expression, indent=False)}"
|
|
4519
4531
|
|
|
4532
|
+
def replacecolumns_sql(self, expression: exp.ReplaceColumns) -> str:
|
|
4533
|
+
return f"REPLACE COLUMNS ({self.expressions(expression, flat=True)})"
|
|
4534
|
+
|
|
4520
4535
|
def addpartition_sql(self, expression: exp.AddPartition) -> str:
|
|
4521
4536
|
exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
|
|
4522
4537
|
location = self.sql(expression, "location")
|
|
@@ -4771,14 +4786,22 @@ class Generator:
|
|
|
4771
4786
|
sqls: list[str] = []
|
|
4772
4787
|
stack: list[None | str | exp.Expr] = [expression]
|
|
4773
4788
|
binary_type = type(expression)
|
|
4789
|
+
is_is = binary_type is exp.Is and op in ("IS", "IS NOT")
|
|
4790
|
+
negate = expression.args.get("negate")
|
|
4774
4791
|
|
|
4775
4792
|
while stack:
|
|
4776
4793
|
node = stack.pop()
|
|
4777
4794
|
|
|
4778
|
-
if type(node) is binary_type
|
|
4795
|
+
if type(node) is binary_type and (
|
|
4796
|
+
node is expression
|
|
4797
|
+
or (is_is and not isinstance(node.expression, exp.Boolean))
|
|
4798
|
+
or node.args.get("negate") == negate
|
|
4799
|
+
):
|
|
4779
4800
|
op_func = node.args.get("operator")
|
|
4780
4801
|
if op_func:
|
|
4781
4802
|
op = f"OPERATOR({self.sql(op_func)})"
|
|
4803
|
+
elif is_is:
|
|
4804
|
+
op = "IS NOT" if node.args.get("negate") else "IS"
|
|
4782
4805
|
|
|
4783
4806
|
stack.append(node.args.get("expression"))
|
|
4784
4807
|
stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
|
|
@@ -6053,6 +6076,13 @@ class Generator:
|
|
|
6053
6076
|
exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
|
|
6054
6077
|
)
|
|
6055
6078
|
|
|
6079
|
+
def arraysort_sql(self, expression: exp.ArraySort) -> str:
|
|
6080
|
+
asc = expression.args.get("asc")
|
|
6081
|
+
nulls_first = expression.args.get("nulls_first")
|
|
6082
|
+
if (asc and asc != exp.true()) or (nulls_first and nulls_first != exp.false()):
|
|
6083
|
+
self.unsupported("ARRAY_SORT only sorts ascending with NULLs last")
|
|
6084
|
+
return self.func("ARRAY_SORT", expression.this, expression.expression)
|
|
6085
|
+
|
|
6056
6086
|
def arraysize_sql(self, expression: exp.ArraySize) -> str:
|
|
6057
6087
|
dim = expression.expression
|
|
6058
6088
|
|
|
@@ -3,10 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
5
|
import typing as t
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
from fractions import Fraction
|
|
6
8
|
|
|
7
9
|
from sqlglot import exp, generator, transforms
|
|
8
10
|
from sqlglot.dialects.dialect import (
|
|
9
11
|
arg_max_or_min_no_count,
|
|
12
|
+
array_sort_order,
|
|
10
13
|
date_add_interval_sql,
|
|
11
14
|
datestrtodate_sql,
|
|
12
15
|
filter_array_using_unnest,
|
|
@@ -176,6 +179,90 @@ def _array_contains_sql(self: BigQueryGenerator, expression: exp.ArrayContains)
|
|
|
176
179
|
)
|
|
177
180
|
|
|
178
181
|
|
|
182
|
+
def _widen_decimal(expression: exp.Expr) -> exp.Expr:
|
|
183
|
+
# NUMERIC holds at most 29 integer and 9 fractional digits, e.g. DECIMAL(38, 0) needs BIGNUMERIC
|
|
184
|
+
for node in expression.find_all(exp.DataType):
|
|
185
|
+
if node.is_type(exp.DType.DECIMAL) and node.expressions:
|
|
186
|
+
precision, *rest = (param.this for param in node.expressions)
|
|
187
|
+
scale = rest[0] if rest else exp.Literal.number(0)
|
|
188
|
+
if precision.is_int and scale.is_int:
|
|
189
|
+
scale_digits = int(scale.name)
|
|
190
|
+
if int(precision.name) - scale_digits > 29 or scale_digits > 9:
|
|
191
|
+
node.set("this", exp.DType.BIGDECIMAL)
|
|
192
|
+
return expression
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _extract_part(part: str) -> t.Callable[[BigQueryGenerator, exp.Func], str]:
|
|
196
|
+
return lambda self, e: self.sql(exp.func("EXTRACT", exp.var(part), e.this, copy=False))
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _day_of_week_iso_sql(self: BigQueryGenerator, expression: exp.DayOfWeekIso) -> str:
|
|
200
|
+
# DAYOFWEEK is Sunday=1..Saturday=7, ISO is Monday=1..Sunday=7
|
|
201
|
+
template: exp.Expr = exp.maybe_parse("(MOD(EXTRACT(DAYOFWEEK FROM :this) + 5, 7) + 1)")
|
|
202
|
+
return self.sql(exp.replace_placeholders(template, this=expression.this))
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _array_agg_fold_sql(
|
|
206
|
+
self: BigQueryGenerator, expression: exp.ArrayDistinct | exp.ArraySort
|
|
207
|
+
) -> str:
|
|
208
|
+
distinct = False
|
|
209
|
+
ignore_nulls = False
|
|
210
|
+
sort: exp.ArraySort | None = None
|
|
211
|
+
agg: exp.Expr = expression
|
|
212
|
+
while isinstance(agg, (exp.ArrayDistinct, exp.ArraySort)) and not agg.expression:
|
|
213
|
+
if isinstance(agg, exp.ArrayDistinct):
|
|
214
|
+
distinct = True
|
|
215
|
+
ignore_nulls |= agg.args.get("check_null") is False
|
|
216
|
+
elif sort is None:
|
|
217
|
+
sort = agg
|
|
218
|
+
agg = agg.this
|
|
219
|
+
|
|
220
|
+
order = array_sort_order(sort) if sort else None
|
|
221
|
+
|
|
222
|
+
# An ARRAY_AGG with its own ORDER BY or LIMIT can't take another
|
|
223
|
+
if (
|
|
224
|
+
not isinstance(agg, exp.ArrayAgg)
|
|
225
|
+
or isinstance(agg.this, (exp.Order, exp.Limit))
|
|
226
|
+
or (sort is not None and order is None)
|
|
227
|
+
):
|
|
228
|
+
self.unsupported(
|
|
229
|
+
f"{expression.sql_name()} is only supported on an ARRAY_AGG without ORDER BY or LIMIT and with a literal order"
|
|
230
|
+
)
|
|
231
|
+
return self.func(expression.sql_name(), expression.this, expression.expression)
|
|
232
|
+
|
|
233
|
+
value = agg.this
|
|
234
|
+
if isinstance(value, exp.Distinct):
|
|
235
|
+
distinct, value = True, value.expressions[0]
|
|
236
|
+
|
|
237
|
+
folded: exp.Expr = exp.Distinct(expressions=[value]) if distinct else value
|
|
238
|
+
if order is not None:
|
|
239
|
+
asc, nulls_first = order
|
|
240
|
+
keys = [exp.Ordered(this=value.copy(), desc=not asc or None, nulls_first=asc)]
|
|
241
|
+
|
|
242
|
+
# Aggregate ORDER BY only puts NULLs first when ascending, so other placements need a key
|
|
243
|
+
if not ignore_nulls and nulls_first is not None and nulls_first != asc:
|
|
244
|
+
if distinct:
|
|
245
|
+
self.unsupported("ARRAY_AGG(DISTINCT) only puts NULLs first when ascending")
|
|
246
|
+
else:
|
|
247
|
+
is_null = value.copy().is_(exp.null())
|
|
248
|
+
keys.insert(
|
|
249
|
+
0,
|
|
250
|
+
exp.Ordered(
|
|
251
|
+
this=is_null, desc=nulls_first or None, nulls_first=not nulls_first
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
folded = exp.Order(this=folded, expressions=keys)
|
|
255
|
+
agg.set("this", folded)
|
|
256
|
+
if ignore_nulls:
|
|
257
|
+
# All-NULL input yields [], but an empty input still yields NULL.
|
|
258
|
+
agg = exp.If(
|
|
259
|
+
this=exp.Count(this=exp.Star()).eq(0),
|
|
260
|
+
true=exp.null(),
|
|
261
|
+
false=exp.func("COALESCE", exp.IgnoreNulls(this=agg), exp.array()),
|
|
262
|
+
)
|
|
263
|
+
return self.sql(agg)
|
|
264
|
+
|
|
265
|
+
|
|
179
266
|
def _ts_or_ds_add_sql(self: BigQueryGenerator, expression: exp.TsOrDsAdd) -> str:
|
|
180
267
|
return date_add_interval_sql("DATE", "ADD")(self, ts_or_ds_add_cast(expression))
|
|
181
268
|
|
|
@@ -311,6 +398,8 @@ class BigQueryGenerator(generator.Generator):
|
|
|
311
398
|
exp.ArgMin: arg_max_or_min_no_count("MIN_BY"),
|
|
312
399
|
exp.Array: inline_array_unless_query,
|
|
313
400
|
exp.ArrayContains: _array_contains_sql,
|
|
401
|
+
exp.ArrayDistinct: _array_agg_fold_sql,
|
|
402
|
+
exp.ArraySort: _array_agg_fold_sql,
|
|
314
403
|
exp.ArrayFilter: filter_array_using_unnest,
|
|
315
404
|
exp.ArrayRemove: filter_array_using_unnest,
|
|
316
405
|
exp.BitwiseAndAgg: rename_func("BIT_AND"),
|
|
@@ -318,7 +407,9 @@ class BigQueryGenerator(generator.Generator):
|
|
|
318
407
|
exp.BitwiseXorAgg: rename_func("BIT_XOR"),
|
|
319
408
|
exp.BitwiseCount: rename_func("BIT_COUNT"),
|
|
320
409
|
exp.ByteLength: rename_func("BYTE_LENGTH"),
|
|
321
|
-
exp.Cast: transforms.preprocess(
|
|
410
|
+
exp.Cast: transforms.preprocess(
|
|
411
|
+
[_widen_decimal, transforms.remove_precision_parameterized_types]
|
|
412
|
+
),
|
|
322
413
|
exp.CollateProperty: lambda self, e: (
|
|
323
414
|
f"DEFAULT COLLATE {self.sql(e, 'this')}"
|
|
324
415
|
if e.args.get("default")
|
|
@@ -336,6 +427,10 @@ class BigQueryGenerator(generator.Generator):
|
|
|
336
427
|
exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"),
|
|
337
428
|
exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"),
|
|
338
429
|
exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"),
|
|
430
|
+
exp.Day: _extract_part("DAY"),
|
|
431
|
+
exp.DayOfMonth: _extract_part("DAY"),
|
|
432
|
+
exp.DayOfWeekIso: _day_of_week_iso_sql,
|
|
433
|
+
exp.DayOfYear: _extract_part("DAYOFYEAR"),
|
|
339
434
|
exp.Filter: no_filter_sql,
|
|
340
435
|
exp.FromTimeZone: lambda self, e: self.func(
|
|
341
436
|
"DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'"
|
|
@@ -346,6 +441,7 @@ class BigQueryGenerator(generator.Generator):
|
|
|
346
441
|
),
|
|
347
442
|
exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))),
|
|
348
443
|
exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"),
|
|
444
|
+
exp.Hour: _extract_part("HOUR"),
|
|
349
445
|
exp.If: if_sql(false_value="NULL"),
|
|
350
446
|
exp.ILike: no_ilike_sql,
|
|
351
447
|
exp.IntDiv: rename_func("DIV"),
|
|
@@ -366,12 +462,14 @@ class BigQueryGenerator(generator.Generator):
|
|
|
366
462
|
exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)),
|
|
367
463
|
exp.MD5Digest: rename_func("MD5"),
|
|
368
464
|
exp.Min: min_or_least,
|
|
465
|
+
exp.Month: _extract_part("MONTH"),
|
|
369
466
|
exp.Normalize: lambda self, e: self.func(
|
|
370
467
|
"NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE",
|
|
371
468
|
e.this,
|
|
372
469
|
e.args.get("form"),
|
|
373
470
|
),
|
|
374
471
|
exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}",
|
|
472
|
+
exp.Quarter: _extract_part("QUARTER"),
|
|
375
473
|
exp.RegexpExtract: lambda self, e: self.func(
|
|
376
474
|
"REGEXP_EXTRACT",
|
|
377
475
|
e.this,
|
|
@@ -431,6 +529,8 @@ class BigQueryGenerator(generator.Generator):
|
|
|
431
529
|
exp.Uuid: lambda *_: "GENERATE_UUID()",
|
|
432
530
|
exp.Values: _derived_table_values_to_unnest,
|
|
433
531
|
exp.VariancePop: rename_func("VAR_POP"),
|
|
532
|
+
exp.WeekOfYear: _extract_part("ISOWEEK"),
|
|
533
|
+
exp.Year: _extract_part("YEAR"),
|
|
434
534
|
exp.SafeDivide: rename_func("SAFE_DIVIDE"),
|
|
435
535
|
}
|
|
436
536
|
|
|
@@ -582,6 +682,47 @@ class BigQueryGenerator(generator.Generator):
|
|
|
582
682
|
"within",
|
|
583
683
|
}
|
|
584
684
|
|
|
685
|
+
def frombase_sql(self, expression: exp.FromBase) -> str:
|
|
686
|
+
# Hex is the only base BigQuery can cast from, and the sign goes before the prefix, e.g. -0xff
|
|
687
|
+
if expression.expression.name != "16":
|
|
688
|
+
self.unsupported("FROM_BASE is only supported for base 16")
|
|
689
|
+
return self.function_fallback_sql(expression)
|
|
690
|
+
|
|
691
|
+
prefixed = exp.func(
|
|
692
|
+
"REGEXP_REPLACE",
|
|
693
|
+
expression.this,
|
|
694
|
+
exp.Literal.string("^([+-]?)"),
|
|
695
|
+
exp.Literal.string("\\10x"),
|
|
696
|
+
copy=False,
|
|
697
|
+
)
|
|
698
|
+
return self.sql(exp.cast(prefixed, exp.DType.BIGINT, copy=False))
|
|
699
|
+
|
|
700
|
+
def approxquantile_sql(self, expression: exp.ApproxQuantile) -> str:
|
|
701
|
+
node: exp.Expr = expression
|
|
702
|
+
while node.parent and not isinstance(node.parent, (exp.Window, exp.AggFunc, exp.Select)):
|
|
703
|
+
node = node.parent
|
|
704
|
+
if isinstance(node.parent, exp.Window) and node.arg_key == "this":
|
|
705
|
+
self.unsupported("APPROX_QUANTILES does not support window functions")
|
|
706
|
+
return self.function_fallback_sql(expression)
|
|
707
|
+
|
|
708
|
+
# The k/n quantile is APPROX_QUANTILES(x, n)[k], e.g. 0.95 -> n = 20, k = 19. n is at most
|
|
709
|
+
# 100,000, so the quantile has at most 5 decimal places, and Decimal comparisons are exact
|
|
710
|
+
quantile = expression.args["quantile"]
|
|
711
|
+
weight = expression.args.get("weight")
|
|
712
|
+
value = Decimal(quantile.to_py()) if quantile.is_number else None
|
|
713
|
+
if (
|
|
714
|
+
value is None
|
|
715
|
+
or not 0 <= value <= 1
|
|
716
|
+
or round(value, 5) != value
|
|
717
|
+
or (weight and (not weight.is_number or weight.to_py() != 1))
|
|
718
|
+
):
|
|
719
|
+
self.unsupported("APPROX_QUANTILE needs a literal quantile and no weight")
|
|
720
|
+
return self.function_fallback_sql(expression)
|
|
721
|
+
|
|
722
|
+
fraction = Fraction(value)
|
|
723
|
+
quantiles = exp.func("APPROX_QUANTILES", expression.this, fraction.denominator, copy=False)
|
|
724
|
+
return self.sql(quantiles[fraction.numerator])
|
|
725
|
+
|
|
585
726
|
def weekstart_sql(self, expression: exp.WeekStart) -> str:
|
|
586
727
|
if expression.this.name.upper() == "SUNDAY":
|
|
587
728
|
# BigQuery specific optimization since WEEK(SUNDAY) == WEEK
|
|
@@ -688,7 +688,8 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
688
688
|
comment_prop = expression.find(exp.SchemaCommentProperty)
|
|
689
689
|
if comment_prop:
|
|
690
690
|
comment_prop.pop()
|
|
691
|
-
query
|
|
691
|
+
if not isinstance(query, exp.Subquery):
|
|
692
|
+
query.replace(exp.paren(query))
|
|
692
693
|
else:
|
|
693
694
|
comment_prop = None
|
|
694
695
|
|
|
@@ -7,6 +7,7 @@ import typing as t
|
|
|
7
7
|
|
|
8
8
|
from sqlglot import exp, generator, transforms
|
|
9
9
|
from sqlglot.dialects.dialect import (
|
|
10
|
+
array_sort_order,
|
|
10
11
|
DATETIME_DELTA,
|
|
11
12
|
JSON_EXTRACT_TYPE,
|
|
12
13
|
approx_count_distinct_sql,
|
|
@@ -548,9 +549,22 @@ def _array_remove_at_sql(self: DuckDBGenerator, expression: exp.ArrayRemoveAt) -
|
|
|
548
549
|
)
|
|
549
550
|
|
|
550
551
|
|
|
551
|
-
@unsupported_args(("expression", "DuckDB's
|
|
552
|
+
@unsupported_args(("expression", "DuckDB's LIST_SORT does not support a comparator."))
|
|
552
553
|
def _array_sort_sql(self: DuckDBGenerator, expression: exp.ArraySort) -> str:
|
|
553
|
-
|
|
554
|
+
order = array_sort_order(expression)
|
|
555
|
+
if order is None:
|
|
556
|
+
self.unsupported("LIST_SORT only takes literal orders")
|
|
557
|
+
return self.func("LIST_SORT", expression.this)
|
|
558
|
+
|
|
559
|
+
asc, nulls_first = order
|
|
560
|
+
if not nulls_first:
|
|
561
|
+
return self.func("LIST_SORT" if asc else "ARRAY_REVERSE_SORT", expression.this)
|
|
562
|
+
return self.func(
|
|
563
|
+
"LIST_SORT",
|
|
564
|
+
expression.this,
|
|
565
|
+
exp.Literal.string("ASC" if asc else "DESC"),
|
|
566
|
+
exp.Literal.string("NULLS FIRST"),
|
|
567
|
+
)
|
|
554
568
|
|
|
555
569
|
|
|
556
570
|
def _array_contains_sql(self: DuckDBGenerator, expression: exp.ArrayContains) -> str:
|
|
@@ -1858,16 +1872,16 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1858
1872
|
"union",
|
|
1859
1873
|
"all",
|
|
1860
1874
|
"when",
|
|
1861
|
-
"
|
|
1875
|
+
"in",
|
|
1862
1876
|
"default",
|
|
1863
|
-
"
|
|
1877
|
+
"create",
|
|
1864
1878
|
"window",
|
|
1865
1879
|
"asymmetric",
|
|
1866
1880
|
"to",
|
|
1867
1881
|
"else",
|
|
1868
1882
|
"localtime",
|
|
1869
1883
|
"from",
|
|
1870
|
-
"
|
|
1884
|
+
"end",
|
|
1871
1885
|
"select",
|
|
1872
1886
|
"current_date",
|
|
1873
1887
|
"foreign",
|
|
@@ -1879,7 +1893,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1879
1893
|
"references",
|
|
1880
1894
|
"fetch",
|
|
1881
1895
|
"limit",
|
|
1882
|
-
"
|
|
1896
|
+
"group",
|
|
1883
1897
|
"leading",
|
|
1884
1898
|
"into",
|
|
1885
1899
|
"collate",
|
|
@@ -1887,13 +1901,13 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1887
1901
|
"do",
|
|
1888
1902
|
"then",
|
|
1889
1903
|
"localtimestamp",
|
|
1890
|
-
"
|
|
1891
|
-
"
|
|
1904
|
+
"check",
|
|
1905
|
+
"lateral",
|
|
1892
1906
|
"current_role",
|
|
1893
1907
|
"where",
|
|
1894
|
-
"
|
|
1908
|
+
"asc",
|
|
1895
1909
|
"placing",
|
|
1896
|
-
"
|
|
1910
|
+
"desc",
|
|
1897
1911
|
"user",
|
|
1898
1912
|
"unique",
|
|
1899
1913
|
"initially",
|
|
@@ -1904,9 +1918,9 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1904
1918
|
"any",
|
|
1905
1919
|
"only",
|
|
1906
1920
|
"deferrable",
|
|
1907
|
-
"
|
|
1921
|
+
"null",
|
|
1908
1922
|
"current_time",
|
|
1909
|
-
"
|
|
1923
|
+
"true",
|
|
1910
1924
|
"table",
|
|
1911
1925
|
"case",
|
|
1912
1926
|
"trailing",
|
|
@@ -1914,7 +1928,7 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1914
1928
|
"for",
|
|
1915
1929
|
"on",
|
|
1916
1930
|
"distinct",
|
|
1917
|
-
"
|
|
1931
|
+
"false",
|
|
1918
1932
|
"not",
|
|
1919
1933
|
"constraint",
|
|
1920
1934
|
"current_timestamp",
|
|
@@ -1930,6 +1944,15 @@ class DuckDBGenerator(generator.Generator):
|
|
|
1930
1944
|
"using",
|
|
1931
1945
|
"order",
|
|
1932
1946
|
"current_catalog",
|
|
1947
|
+
"describe",
|
|
1948
|
+
"lambda",
|
|
1949
|
+
"pivot",
|
|
1950
|
+
"pivot_longer",
|
|
1951
|
+
"pivot_wider",
|
|
1952
|
+
"qualify",
|
|
1953
|
+
"show",
|
|
1954
|
+
"summarize",
|
|
1955
|
+
"unpivot",
|
|
1933
1956
|
}
|
|
1934
1957
|
|
|
1935
1958
|
UNWRAPPED_INTERVAL_VALUES = (exp.Literal, exp.Paren)
|
|
@@ -2674,33 +2697,6 @@ class DuckDBGenerator(generator.Generator):
|
|
|
2674
2697
|
self.unsupported("SOUNDEX is not supported in DuckDB")
|
|
2675
2698
|
return self.func("SOUNDEX", expression.this)
|
|
2676
2699
|
|
|
2677
|
-
def sortarray_sql(self, expression: exp.SortArray) -> str:
|
|
2678
|
-
arr = expression.this
|
|
2679
|
-
asc = expression.args.get("asc")
|
|
2680
|
-
nulls_first = expression.args.get("nulls_first")
|
|
2681
|
-
|
|
2682
|
-
if not isinstance(asc, exp.Boolean) and not isinstance(nulls_first, exp.Boolean):
|
|
2683
|
-
return self.func("LIST_SORT", arr, asc, nulls_first)
|
|
2684
|
-
|
|
2685
|
-
nulls_are_first = nulls_first == exp.true()
|
|
2686
|
-
nulls_first_sql = exp.Literal.string("NULLS FIRST") if nulls_are_first else None
|
|
2687
|
-
|
|
2688
|
-
if not isinstance(asc, exp.Boolean):
|
|
2689
|
-
return self.func("LIST_SORT", arr, asc, nulls_first_sql)
|
|
2690
|
-
|
|
2691
|
-
descending = asc == exp.false()
|
|
2692
|
-
|
|
2693
|
-
if not descending and not nulls_are_first:
|
|
2694
|
-
return self.func("LIST_SORT", arr)
|
|
2695
|
-
if not nulls_are_first:
|
|
2696
|
-
return self.func("ARRAY_REVERSE_SORT", arr)
|
|
2697
|
-
return self.func(
|
|
2698
|
-
"LIST_SORT",
|
|
2699
|
-
arr,
|
|
2700
|
-
exp.Literal.string("DESC" if descending else "ASC"),
|
|
2701
|
-
exp.Literal.string("NULLS FIRST"),
|
|
2702
|
-
)
|
|
2703
|
-
|
|
2704
2700
|
def install_sql(self, expression: exp.Install) -> str:
|
|
2705
2701
|
force = "FORCE " if expression.args.get("force") else ""
|
|
2706
2702
|
this = self.sql(expression, "this")
|
|
@@ -121,6 +121,11 @@ class FabricGenerator(TSQLGenerator):
|
|
|
121
121
|
# Generate the AT TIME ZONE expression (which will handle the inner cast conversion)
|
|
122
122
|
at_time_zone_sql = super().attimezone_sql(expression)
|
|
123
123
|
|
|
124
|
+
# Don't wrap again if it's already cast to DATETIME2, e.g. when re-parsing our own output
|
|
125
|
+
parent = expression.parent
|
|
126
|
+
if isinstance(parent, exp.Cast) and parent.to.is_type(exp.DType.DATETIME2):
|
|
127
|
+
return at_time_zone_sql
|
|
128
|
+
|
|
124
129
|
# Wrap it in an outer cast to DATETIME2
|
|
125
130
|
return f"CAST({at_time_zone_sql} AS DATETIME2({precision}))"
|
|
126
131
|
|
|
@@ -6,6 +6,7 @@ from functools import partial
|
|
|
6
6
|
|
|
7
7
|
from sqlglot import exp, generator, transforms
|
|
8
8
|
from sqlglot.dialects.dialect import (
|
|
9
|
+
array_sort_order,
|
|
9
10
|
DATE_ADD_OR_SUB,
|
|
10
11
|
approx_count_distinct_sql,
|
|
11
12
|
arg_max_or_min_no_count,
|
|
@@ -162,6 +163,9 @@ def _date_diff_sql(self: HiveGenerator, expression: exp.DateDiff | exp.TsOrDsDif
|
|
|
162
163
|
|
|
163
164
|
@generator.unsupported_args(("expression", "Hive's SORT_ARRAY does not support a comparator."))
|
|
164
165
|
def _array_sort_sql(self: HiveGenerator, expression: exp.ArraySort) -> str:
|
|
166
|
+
asc, nulls_first = array_sort_order(expression) or (False, False)
|
|
167
|
+
if not asc or nulls_first is False:
|
|
168
|
+
self.unsupported("Hive's SORT_ARRAY only sorts ascending with NULLs first")
|
|
165
169
|
return self.func("SORT_ARRAY", expression.this)
|
|
166
170
|
|
|
167
171
|
|
|
@@ -195,6 +195,7 @@ class MySQLGenerator(generator.Generator):
|
|
|
195
195
|
transforms.unnest_generate_date_array_using_recursive_cte,
|
|
196
196
|
]
|
|
197
197
|
),
|
|
198
|
+
exp.Stddev: rename_func("STDDEV_SAMP"),
|
|
198
199
|
exp.StrPosition: lambda self, e: strposition_sql(
|
|
199
200
|
self, e, func_name="LOCATE", supports_position=True
|
|
200
201
|
),
|
|
@@ -228,6 +229,8 @@ class MySQLGenerator(generator.Generator):
|
|
|
228
229
|
exp.TsOrDsToDate: _ts_or_ds_to_date_sql,
|
|
229
230
|
exp.Unicode: lambda self, e: f"ORD(CONVERT({self.sql(e.this)} USING utf32))",
|
|
230
231
|
exp.UnixToTime: _unix_to_time_sql,
|
|
232
|
+
exp.Variance: rename_func("VAR_SAMP"),
|
|
233
|
+
exp.VariancePop: rename_func("VAR_POP"),
|
|
231
234
|
exp.Week: remove_ts_or_ds_to_date(),
|
|
232
235
|
exp.WeekOfYear: remove_ts_or_ds_to_date(rename_func("WEEKOFYEAR")),
|
|
233
236
|
exp.Year: remove_ts_or_ds_to_date(),
|
|
@@ -40,7 +40,7 @@ from sqlglot.dialects.dialect import (
|
|
|
40
40
|
ts_or_ds_add_cast,
|
|
41
41
|
)
|
|
42
42
|
from sqlglot.generator import unsupported_args
|
|
43
|
-
from sqlglot.helper import ensure_list, seq_get
|
|
43
|
+
from sqlglot.helper import ensure_list, seq_get, to_int
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
DATE_DIFF_FACTOR = {
|
|
@@ -579,8 +579,13 @@ class PostgresGenerator(generator.Generator):
|
|
|
579
579
|
|
|
580
580
|
this = expression.this
|
|
581
581
|
if unit.startswith("quarter") and isinstance(this, exp.Literal):
|
|
582
|
-
|
|
583
|
-
|
|
582
|
+
# Postgres has no QUARTER unit and no exponents in interval strings
|
|
583
|
+
quarters = to_int(this.to_py())
|
|
584
|
+
if quarters is None:
|
|
585
|
+
self.unsupported("Postgres intervals need a whole number of quarters")
|
|
586
|
+
else:
|
|
587
|
+
this.replace(exp.Literal.string(quarters * 3))
|
|
588
|
+
expression.args["unit"].replace(exp.var("MONTH"))
|
|
584
589
|
|
|
585
590
|
return super().interval_sql(expression)
|
|
586
591
|
|