sqlglotc 30.8.0__tar.gz → 30.10.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.8.0/sqlglotc.egg-info → sqlglotc-30.10.0}/PKG-INFO +2 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/pyproject.toml +2 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/aggregate.py +1 -1
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/core.py +33 -12
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/datatypes.py +17 -5
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/ddl.py +8 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/dml.py +1 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/functions.py +10 -1
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/properties.py +4 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/query.py +12 -4
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/string.py +1 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generator.py +166 -13
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/bigquery.py +6 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/clickhouse.py +1 -1
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/databricks.py +4 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/duckdb.py +60 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/exasol.py +6 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/mysql.py +37 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/oracle.py +10 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/presto.py +2 -6
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/snowflake.py +0 -3
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/sqlite.py +39 -10
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/starrocks.py +5 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/tsql.py +18 -7
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/annotate_types.py +10 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/qualify_columns.py +2 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/simplify.py +31 -3
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parser.py +134 -23
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/bigquery.py +29 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/clickhouse.py +9 -3
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/databricks.py +5 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/duckdb.py +1 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/oracle.py +21 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/snowflake.py +16 -1
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/spark2.py +1 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/tsql.py +72 -10
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/serde.py +1 -1
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/tokenizer_core.py +1 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0/sqlglotc.egg-info}/PKG-INFO +2 -2
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/requires.txt +1 -1
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/MANIFEST.in +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/setup.cfg +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/setup.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/array.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/builders.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/constraints.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/json.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/temporal.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/dremio.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/hive.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/postgres.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/python.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/singlestore.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/spark.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/spark2.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/teradata.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/trino.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/lineage.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/qualify_tables.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/resolver.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/scope.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/dremio.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/drill.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/druid.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/exasol.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/hive.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/mysql.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/postgres.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/presto.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/prql.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/spark.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/sqlite.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/starrocks.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/teradata.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/trino.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/schema.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglotc
|
|
3
|
-
Version: 30.
|
|
3
|
+
Version: 30.10.0
|
|
4
4
|
Summary: mypyc-compiled extensions for sqlglot
|
|
5
5
|
Author-email: Toby Mao <toby.mao@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,4 +10,4 @@ Requires-Python: >=3.10
|
|
|
10
10
|
Provides-Extra: dev
|
|
11
11
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
12
12
|
Requires-Dist: setuptools_scm; extra == "dev"
|
|
13
|
-
Requires-Dist: sqlglot-mypy>=1.
|
|
13
|
+
Requires-Dist: sqlglot-mypy>=2.1.0.post2; extra == "dev"
|
|
@@ -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 >= 1.
|
|
10
|
+
dev = ["setuptools >= 61.0", "setuptools_scm", "sqlglot-mypy >= 2.1.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 >= 1.
|
|
20
|
+
"sqlglot-mypy >= 2.1.0.post2",
|
|
21
21
|
"types-python-dateutil",
|
|
22
22
|
"sqlglot",
|
|
23
23
|
]
|
|
@@ -88,6 +88,7 @@ class Expr:
|
|
|
88
88
|
_hash_raw_args: t.ClassVar[bool] = False
|
|
89
89
|
is_subquery: t.ClassVar[bool] = False
|
|
90
90
|
is_cast: t.ClassVar[bool] = False
|
|
91
|
+
is_data_type: t.ClassVar[bool] = False
|
|
91
92
|
|
|
92
93
|
args: dict[str, t.Any]
|
|
93
94
|
parent: Expr | None
|
|
@@ -837,26 +838,35 @@ class Expression(Expr):
|
|
|
837
838
|
def __hash__(self) -> int:
|
|
838
839
|
if self._hash is None:
|
|
839
840
|
nodes: list[Expr] = []
|
|
840
|
-
|
|
841
|
-
queue.append(self)
|
|
841
|
+
stack: list[Expr] = [self]
|
|
842
842
|
|
|
843
|
-
|
|
844
|
-
|
|
843
|
+
# Collect nodes, finding child expressions inline instead of via the
|
|
844
|
+
# iter_expressions generator (whose per-node generator object dominates the
|
|
845
|
+
# hash's cost). reversed(nodes) is a valid post-order regardless of DFS/BFS.
|
|
846
|
+
while stack:
|
|
847
|
+
node = stack.pop()
|
|
845
848
|
nodes.append(node)
|
|
846
849
|
|
|
847
|
-
for
|
|
848
|
-
if
|
|
849
|
-
|
|
850
|
+
for v in node.args.values():
|
|
851
|
+
if isinstance(v, Expr):
|
|
852
|
+
if v._hash is None:
|
|
853
|
+
stack.append(v)
|
|
854
|
+
elif type(v) is list:
|
|
855
|
+
for x in v:
|
|
856
|
+
if isinstance(x, Expr) and x._hash is None:
|
|
857
|
+
stack.append(x)
|
|
850
858
|
|
|
851
859
|
for node in reversed(nodes):
|
|
852
860
|
hash_ = hash(node.key)
|
|
853
861
|
|
|
854
862
|
if node._hash_raw_args:
|
|
855
|
-
for k
|
|
863
|
+
for k in sorted(node.args):
|
|
864
|
+
v = node.args[k]
|
|
856
865
|
if v:
|
|
857
866
|
hash_ = hash((hash_, k, v))
|
|
858
867
|
else:
|
|
859
|
-
for k
|
|
868
|
+
for k in sorted(node.args):
|
|
869
|
+
v = node.args[k]
|
|
860
870
|
vt = type(v)
|
|
861
871
|
|
|
862
872
|
if vt is list:
|
|
@@ -953,6 +963,8 @@ class Expression(Expr):
|
|
|
953
963
|
|
|
954
964
|
@property
|
|
955
965
|
def type(self) -> DataType | None:
|
|
966
|
+
if self.is_data_type:
|
|
967
|
+
return self # type: ignore[return-value]
|
|
956
968
|
if self.is_cast:
|
|
957
969
|
return self._type or self.to # type: ignore[attr-defined]
|
|
958
970
|
return self._type
|
|
@@ -1039,6 +1051,11 @@ class Expression(Expr):
|
|
|
1039
1051
|
return comments
|
|
1040
1052
|
|
|
1041
1053
|
def append(self, arg_key: str, value: t.Any) -> None:
|
|
1054
|
+
node: Expr | None = self
|
|
1055
|
+
while node and node._hash is not None:
|
|
1056
|
+
node._hash = None
|
|
1057
|
+
node = node.parent
|
|
1058
|
+
|
|
1042
1059
|
if type(self.args.get(arg_key)) is not list:
|
|
1043
1060
|
self.args[arg_key] = []
|
|
1044
1061
|
self._set_parent(arg_key, value)
|
|
@@ -2118,6 +2135,10 @@ class Distance(Expression, Binary):
|
|
|
2118
2135
|
pass
|
|
2119
2136
|
|
|
2120
2137
|
|
|
2138
|
+
class DistanceNd(Expression, Binary):
|
|
2139
|
+
pass
|
|
2140
|
+
|
|
2141
|
+
|
|
2121
2142
|
class Escape(Expression, Binary):
|
|
2122
2143
|
pass
|
|
2123
2144
|
|
|
@@ -2269,8 +2290,7 @@ class Or(Expression, Connector, Func):
|
|
|
2269
2290
|
|
|
2270
2291
|
|
|
2271
2292
|
class Xor(Expression, Connector, Func):
|
|
2272
|
-
arg_types = {"this":
|
|
2273
|
-
is_var_len_args = True
|
|
2293
|
+
arg_types = {"this": True, "expression": True, "round_input": False}
|
|
2274
2294
|
|
|
2275
2295
|
|
|
2276
2296
|
class Pow(Expression, Binary, Func):
|
|
@@ -2428,6 +2448,7 @@ QUERY_MODIFIERS = {
|
|
|
2428
2448
|
"settings": False,
|
|
2429
2449
|
"format": False,
|
|
2430
2450
|
"options": False,
|
|
2451
|
+
"for_": False,
|
|
2431
2452
|
}
|
|
2432
2453
|
|
|
2433
2454
|
|
|
@@ -2534,7 +2555,7 @@ def _to_s(node: t.Any, verbose: bool = False, level: int = 0, repr_str: bool = F
|
|
|
2534
2555
|
if isinstance(node, Expr):
|
|
2535
2556
|
args = {k: v for k, v in node.args.items() if (v is not None and v != []) or verbose}
|
|
2536
2557
|
|
|
2537
|
-
if (node.type or verbose) and
|
|
2558
|
+
if (node.type or verbose) and not node.is_data_type:
|
|
2538
2559
|
args["_type"] = node.type
|
|
2539
2560
|
if node.comments or verbose:
|
|
2540
2561
|
args["_comments"] = node.comments
|
|
@@ -8,6 +8,7 @@ from enum import auto
|
|
|
8
8
|
from sqlglot.helper import AutoName
|
|
9
9
|
from sqlglot.errors import ErrorLevel, ParseError
|
|
10
10
|
from sqlglot.expressions.core import (
|
|
11
|
+
Expr,
|
|
11
12
|
Expression,
|
|
12
13
|
_TimeUnit,
|
|
13
14
|
Identifier,
|
|
@@ -17,13 +18,18 @@ from sqlglot.expressions.core import (
|
|
|
17
18
|
from builtins import type as Type
|
|
18
19
|
|
|
19
20
|
if t.TYPE_CHECKING:
|
|
21
|
+
from sqlglot._typing import DataTypeArgs
|
|
20
22
|
from sqlglot.dialects.dialect import DialectType
|
|
21
|
-
from typing_extensions import Self
|
|
23
|
+
from typing_extensions import Self, Unpack
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class DataTypeParam(Expression):
|
|
25
27
|
arg_types = {"this": True, "expression": False}
|
|
26
28
|
|
|
29
|
+
@property
|
|
30
|
+
def this(self) -> Expr:
|
|
31
|
+
return self.args["this"]
|
|
32
|
+
|
|
27
33
|
@property
|
|
28
34
|
def name(self) -> str:
|
|
29
35
|
return self.this.name
|
|
@@ -181,6 +187,8 @@ class DataType(Expression):
|
|
|
181
187
|
"collate": False,
|
|
182
188
|
}
|
|
183
189
|
|
|
190
|
+
is_data_type: t.ClassVar[bool] = True
|
|
191
|
+
|
|
184
192
|
Type: t.ClassVar[Type[DType]] = DType
|
|
185
193
|
|
|
186
194
|
STRUCT_TYPES: t.ClassVar[set[DType]] = {
|
|
@@ -331,7 +339,7 @@ class DataType(Expression):
|
|
|
331
339
|
dialect: DialectType = None,
|
|
332
340
|
udt: bool = False,
|
|
333
341
|
copy: bool = True,
|
|
334
|
-
**kwargs:
|
|
342
|
+
**kwargs: Unpack[DataTypeArgs],
|
|
335
343
|
) -> Self:
|
|
336
344
|
"""
|
|
337
345
|
Constructs a DataType object.
|
|
@@ -364,7 +372,11 @@ class DataType(Expression):
|
|
|
364
372
|
|
|
365
373
|
@classmethod
|
|
366
374
|
def from_str(
|
|
367
|
-
cls,
|
|
375
|
+
cls,
|
|
376
|
+
dtype: str,
|
|
377
|
+
dialect: DialectType = None,
|
|
378
|
+
udt: bool = False,
|
|
379
|
+
**kwargs: Unpack[DataTypeArgs],
|
|
368
380
|
) -> Self:
|
|
369
381
|
"""
|
|
370
382
|
Constructs a `DataType` object from a `str` representation.
|
|
@@ -405,10 +417,10 @@ class DataType(Expression):
|
|
|
405
417
|
Returns:
|
|
406
418
|
True, if and only if there is a type in `dtypes` which is equal to this DataType.
|
|
407
419
|
"""
|
|
408
|
-
self_is_nullable = self.args.get("nullable")
|
|
420
|
+
self_is_nullable: bool | None = self.args.get("nullable")
|
|
409
421
|
for dtype in dtypes:
|
|
410
422
|
other_type = DataType.build(dtype, copy=False, udt=True)
|
|
411
|
-
other_is_nullable = other_type.args.get("nullable")
|
|
423
|
+
other_is_nullable: bool | None = other_type.args.get("nullable")
|
|
412
424
|
if (
|
|
413
425
|
other_type.expressions
|
|
414
426
|
or (check_nullable and (self_is_nullable or other_is_nullable))
|
|
@@ -226,6 +226,14 @@ class UserDefinedFunction(Expression):
|
|
|
226
226
|
arg_types = {"this": True, "expressions": False, "wrapped": False}
|
|
227
227
|
|
|
228
228
|
|
|
229
|
+
class MacroOverloads(Expression):
|
|
230
|
+
arg_types = {"expressions": True}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class MacroOverload(Expression):
|
|
234
|
+
arg_types = {"this": True, "expressions": False, "is_table": False}
|
|
235
|
+
|
|
236
|
+
|
|
229
237
|
class CharacterSet(Expression):
|
|
230
238
|
arg_types = {"this": True, "default": False}
|
|
231
239
|
|
|
@@ -70,7 +70,12 @@ class Cast(Expression, Func):
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
class TryCast(Cast):
|
|
73
|
-
arg_types = {
|
|
73
|
+
arg_types = {
|
|
74
|
+
**Cast.arg_types,
|
|
75
|
+
"requires_string": False,
|
|
76
|
+
"null_on_text_overflow": False,
|
|
77
|
+
"probe_date_format": False,
|
|
78
|
+
}
|
|
74
79
|
|
|
75
80
|
|
|
76
81
|
class JSONCast(Cast):
|
|
@@ -436,6 +441,10 @@ class XMLElement(Expression, Func):
|
|
|
436
441
|
arg_types = {"this": True, "expressions": False, "evalname": False}
|
|
437
442
|
|
|
438
443
|
|
|
444
|
+
class GetIgnoreCase(Expression, Func):
|
|
445
|
+
arg_types = {"this": True, "expression": True}
|
|
446
|
+
|
|
447
|
+
|
|
439
448
|
class XMLGet(Expression, Func):
|
|
440
449
|
_sql_names = ["XMLGET"]
|
|
441
450
|
arg_types = {"this": True, "expression": True, "instance": False}
|
|
@@ -75,6 +75,10 @@ class ChecksumProperty(Property):
|
|
|
75
75
|
arg_types = {"on": False, "default": False}
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
class ClusterProperty(Property):
|
|
79
|
+
arg_types = {"this": False, "expressions": False}
|
|
80
|
+
|
|
81
|
+
|
|
78
82
|
class CollateProperty(Property):
|
|
79
83
|
arg_types = {"this": True, "default": False}
|
|
80
84
|
|
|
@@ -859,8 +859,8 @@ class SkipJSONColumn(Expression):
|
|
|
859
859
|
arg_types = {"regexp": False, "expression": True}
|
|
860
860
|
|
|
861
861
|
|
|
862
|
-
class Cluster(
|
|
863
|
-
|
|
862
|
+
class Cluster(Expression):
|
|
863
|
+
arg_types = {"expressions": True}
|
|
864
864
|
|
|
865
865
|
|
|
866
866
|
class Distribute(Order):
|
|
@@ -915,6 +915,11 @@ class QueryOption(Expression):
|
|
|
915
915
|
arg_types = {"this": True, "expression": False}
|
|
916
916
|
|
|
917
917
|
|
|
918
|
+
# FOR { XML | JSON } query modifier; `kind` is the discriminant ("XML" or "JSON").
|
|
919
|
+
class ForClause(Expression):
|
|
920
|
+
arg_types = {"kind": True, "expressions": False}
|
|
921
|
+
|
|
922
|
+
|
|
918
923
|
class WithTableHint(Expression):
|
|
919
924
|
arg_types = {"expressions": True}
|
|
920
925
|
|
|
@@ -1266,8 +1271,8 @@ class Select(Expression, Query):
|
|
|
1266
1271
|
Set the CLUSTER BY expression.
|
|
1267
1272
|
|
|
1268
1273
|
Example:
|
|
1269
|
-
>>> Select().from_("tbl").select("x").cluster_by("x
|
|
1270
|
-
'SELECT x FROM tbl CLUSTER BY x
|
|
1274
|
+
>>> Select().from_("tbl").select("x").cluster_by("x").sql(dialect="hive")
|
|
1275
|
+
'SELECT x FROM tbl CLUSTER BY x'
|
|
1271
1276
|
|
|
1272
1277
|
Args:
|
|
1273
1278
|
*expressions: the SQL code strings to parse.
|
|
@@ -1753,6 +1758,9 @@ class Pivot(Expression):
|
|
|
1753
1758
|
"default_on_null": False,
|
|
1754
1759
|
"into": False,
|
|
1755
1760
|
"with_": False,
|
|
1761
|
+
"identify_pivot_strings": False,
|
|
1762
|
+
"prefixed_pivot_columns": False,
|
|
1763
|
+
"pivot_column_naming": False,
|
|
1756
1764
|
}
|
|
1757
1765
|
|
|
1758
1766
|
@property
|
|
@@ -676,6 +676,7 @@ class Generator:
|
|
|
676
676
|
exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
|
|
677
677
|
exp.Cluster: exp.Properties.Location.POST_SCHEMA,
|
|
678
678
|
exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
|
|
679
|
+
exp.ClusterProperty: exp.Properties.Location.POST_SCHEMA,
|
|
679
680
|
exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
|
|
680
681
|
exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
|
|
681
682
|
exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
|
|
@@ -1328,7 +1329,9 @@ class Generator:
|
|
|
1328
1329
|
if expression_sql:
|
|
1329
1330
|
expression_sql = f"{begin}{self.sep()}{expression_sql}"
|
|
1330
1331
|
|
|
1331
|
-
if
|
|
1332
|
+
if not isinstance(expression.expression, exp.MacroOverloads) and (
|
|
1333
|
+
self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return)
|
|
1334
|
+
):
|
|
1332
1335
|
postalias_props_sql = ""
|
|
1333
1336
|
if properties_locs.get(exp.Properties.Location.POST_ALIAS):
|
|
1334
1337
|
postalias_props_sql = self.properties(
|
|
@@ -1605,7 +1608,12 @@ class Generator:
|
|
|
1605
1608
|
)
|
|
1606
1609
|
|
|
1607
1610
|
return delimited_byte_string
|
|
1608
|
-
|
|
1611
|
+
|
|
1612
|
+
if "\\" in self.dialect.tokenizer_class.STRING_ESCAPES:
|
|
1613
|
+
return self.sql(exp.Literal.string(this))
|
|
1614
|
+
|
|
1615
|
+
self.unsupported(f"Byte strings are not supported for {self.dialect.__class__.__name__}")
|
|
1616
|
+
return ""
|
|
1609
1617
|
|
|
1610
1618
|
def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
|
|
1611
1619
|
this = self.sql(expression, "this")
|
|
@@ -2454,6 +2462,81 @@ class Generator:
|
|
|
2454
2462
|
|
|
2455
2463
|
return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
|
|
2456
2464
|
|
|
2465
|
+
def _pivot_in_value_aliases(self, expression: exp.Pivot) -> list[exp.Expression] | None:
|
|
2466
|
+
# Returns the rewritten field.expressions list with PivotAlias wrappers injected where
|
|
2467
|
+
# the stored column name differs from the target dialect's natural output.
|
|
2468
|
+
columns = expression.args.get("columns")
|
|
2469
|
+
if not columns or len(expression.fields) != 1:
|
|
2470
|
+
return None
|
|
2471
|
+
|
|
2472
|
+
args = expression.args
|
|
2473
|
+
parser_cls = self.dialect.parser_class
|
|
2474
|
+
|
|
2475
|
+
tgt_identify_pivot_strings = parser_cls.IDENTIFY_PIVOT_STRINGS
|
|
2476
|
+
tgt_prefixed_pivot_columns = parser_cls.PREFIXED_PIVOT_COLUMNS
|
|
2477
|
+
tgt_pivot_column_naming = parser_cls.PIVOT_COLUMN_NAMING
|
|
2478
|
+
|
|
2479
|
+
src_identify_pivot_strings = args.get("identify_pivot_strings", tgt_identify_pivot_strings)
|
|
2480
|
+
src_prefixed_pivot_columns = args.get("prefixed_pivot_columns", tgt_prefixed_pivot_columns)
|
|
2481
|
+
src_pivot_column_naming = args.get("pivot_column_naming", tgt_pivot_column_naming)
|
|
2482
|
+
|
|
2483
|
+
if (
|
|
2484
|
+
src_identify_pivot_strings == tgt_identify_pivot_strings
|
|
2485
|
+
and src_prefixed_pivot_columns == tgt_prefixed_pivot_columns
|
|
2486
|
+
and src_pivot_column_naming == tgt_pivot_column_naming
|
|
2487
|
+
):
|
|
2488
|
+
return None
|
|
2489
|
+
|
|
2490
|
+
in_exprs = expression.fields[0].expressions
|
|
2491
|
+
step = len(columns) // len(in_exprs)
|
|
2492
|
+
|
|
2493
|
+
# Derive the per-value suffix from the first stored column vs the first IN-list value.
|
|
2494
|
+
# This correctly handles dialects (e.g. Spark single-agg) that ignore agg aliases.
|
|
2495
|
+
first_base = in_exprs[0].sql() if src_identify_pivot_strings else in_exprs[0].alias_or_name
|
|
2496
|
+
first_stored = columns[0].name
|
|
2497
|
+
|
|
2498
|
+
# exit if only suffix matches, not prefix. (e.g. BigQuery, which cannot be fixed)
|
|
2499
|
+
if not first_stored.startswith(first_base):
|
|
2500
|
+
return None
|
|
2501
|
+
|
|
2502
|
+
suffix = first_stored[len(first_base) :]
|
|
2503
|
+
|
|
2504
|
+
# Whether the target dialect would append an agg-name suffix for this pivot.
|
|
2505
|
+
# Spark single-agg uniquely drops the agg alias entirely.
|
|
2506
|
+
target_has_suffix = (
|
|
2507
|
+
len(expression.expressions) > 1 or tgt_pivot_column_naming != "agg_name_if_multiple"
|
|
2508
|
+
) and any(a.alias for a in expression.expressions)
|
|
2509
|
+
source_has_suffix = suffix != ""
|
|
2510
|
+
|
|
2511
|
+
new_exprs: list[exp.Expression] = []
|
|
2512
|
+
modified = False
|
|
2513
|
+
for val_idx, e in enumerate(in_exprs):
|
|
2514
|
+
if isinstance(e, exp.PivotAlias):
|
|
2515
|
+
new_exprs.append(e)
|
|
2516
|
+
continue
|
|
2517
|
+
|
|
2518
|
+
i = val_idx * step
|
|
2519
|
+
stored_full = columns[i].name
|
|
2520
|
+
stored_value = stored_full[: -len(suffix)] if suffix else stored_full
|
|
2521
|
+
target_value = e.sql() if tgt_identify_pivot_strings else e.alias_or_name
|
|
2522
|
+
|
|
2523
|
+
# Source had a suffix, but target won't apply one
|
|
2524
|
+
if source_has_suffix and not target_has_suffix:
|
|
2525
|
+
new_exprs.append(
|
|
2526
|
+
exp.PivotAlias(this=e, alias=exp.to_identifier(stored_full, quoted=True))
|
|
2527
|
+
)
|
|
2528
|
+
modified = True
|
|
2529
|
+
# Value-part mismatch (e.g. Snowflake's literal-style values vs others).
|
|
2530
|
+
elif stored_value != target_value:
|
|
2531
|
+
new_exprs.append(
|
|
2532
|
+
exp.PivotAlias(this=e, alias=exp.to_identifier(stored_value, quoted=True))
|
|
2533
|
+
)
|
|
2534
|
+
modified = True
|
|
2535
|
+
else:
|
|
2536
|
+
new_exprs.append(e)
|
|
2537
|
+
|
|
2538
|
+
return new_exprs if modified else None
|
|
2539
|
+
|
|
2457
2540
|
def pivot_sql(self, expression: exp.Pivot) -> str:
|
|
2458
2541
|
expressions = self.expressions(expression, flat=True)
|
|
2459
2542
|
direction = "UNPIVOT" if expression.unpivot else "PIVOT"
|
|
@@ -2473,6 +2556,12 @@ class Generator:
|
|
|
2473
2556
|
sql = f"{direction} {this}{on}{into}{using}{group}"
|
|
2474
2557
|
return self.prepend_ctes(expression, sql)
|
|
2475
2558
|
|
|
2559
|
+
if not expression.unpivot:
|
|
2560
|
+
# Wrap IN-list values with explicit aliases where the target dialect would differ
|
|
2561
|
+
new_field_exprs = self._pivot_in_value_aliases(expression)
|
|
2562
|
+
if new_field_exprs is not None:
|
|
2563
|
+
expression.fields[0].set("expressions", new_field_exprs)
|
|
2564
|
+
|
|
2476
2565
|
alias = self.sql(expression, "alias")
|
|
2477
2566
|
alias = f" AS {alias}" if alias else ""
|
|
2478
2567
|
|
|
@@ -2896,7 +2985,12 @@ class Generator:
|
|
|
2896
2985
|
if files:
|
|
2897
2986
|
files_sql = self.expressions(files, flat=True)
|
|
2898
2987
|
files_sql = f"FILES{self.wrap(files_sql)}"
|
|
2899
|
-
|
|
2988
|
+
if is_overwrite:
|
|
2989
|
+
this = f" {this}"
|
|
2990
|
+
elif expression.args.get("temp"):
|
|
2991
|
+
this = f" INTO TEMP TABLE {this}"
|
|
2992
|
+
else:
|
|
2993
|
+
this = f" INTO TABLE {this}"
|
|
2900
2994
|
return f"LOAD DATA{overwrite}{this} FROM {files_sql}"
|
|
2901
2995
|
|
|
2902
2996
|
local = " LOCAL" if expression.args.get("local") else ""
|
|
@@ -2949,12 +3043,53 @@ class Generator:
|
|
|
2949
3043
|
def cluster_sql(self, expression: exp.Cluster) -> str:
|
|
2950
3044
|
return self.op_expressions("CLUSTER BY", expression)
|
|
2951
3045
|
|
|
3046
|
+
def clusterproperty_sql(self, expression: exp.ClusterProperty) -> str:
|
|
3047
|
+
if expression.this:
|
|
3048
|
+
self.unsupported(f"Unsupported CLUSTER BY {self.sql(expression, 'this')}")
|
|
3049
|
+
return ""
|
|
3050
|
+
expressions = self.expressions(expression, flat=True)
|
|
3051
|
+
return f"CLUSTER BY ({expressions})"
|
|
3052
|
+
|
|
2952
3053
|
def distribute_sql(self, expression: exp.Distribute) -> str:
|
|
2953
3054
|
return self.op_expressions("DISTRIBUTE BY", expression)
|
|
2954
3055
|
|
|
2955
3056
|
def sort_sql(self, expression: exp.Sort) -> str:
|
|
2956
3057
|
return self.op_expressions("SORT BY", expression)
|
|
2957
3058
|
|
|
3059
|
+
def _resolve_ordered_for_null_ordering_simulation(
|
|
3060
|
+
self, expression: exp.Ordered
|
|
3061
|
+
) -> exp.Expr | None:
|
|
3062
|
+
"""Resolve a bare ORDER BY name against the enclosing SELECT projection.
|
|
3063
|
+
|
|
3064
|
+
Returns the underlying expression of the uniquely-matching projection
|
|
3065
|
+
(Alias-stripped) for substitution into the NULLS FIRST/LAST CASE
|
|
3066
|
+
simulation, since the CASE is evaluated in FROM-clause scope rather
|
|
3067
|
+
than alias scope (MySQL error 1052). Returns None if no safe
|
|
3068
|
+
substitution applies, leaving the original behaviour unchanged.
|
|
3069
|
+
"""
|
|
3070
|
+
this = expression.this
|
|
3071
|
+
if not (isinstance(this, exp.Column) and not this.table):
|
|
3072
|
+
return None
|
|
3073
|
+
|
|
3074
|
+
ancestor = expression.find_ancestor(exp.Select, exp.Window)
|
|
3075
|
+
if not isinstance(ancestor, exp.Select):
|
|
3076
|
+
return None
|
|
3077
|
+
|
|
3078
|
+
column_name = this.name
|
|
3079
|
+
matched: list[exp.Expr] = [
|
|
3080
|
+
p.this if isinstance(p, exp.Alias) else p
|
|
3081
|
+
for p in ancestor.selects
|
|
3082
|
+
if p.output_name == column_name
|
|
3083
|
+
]
|
|
3084
|
+
match = matched[0] if len(matched) == 1 else None
|
|
3085
|
+
|
|
3086
|
+
# Skip the substitution when it would be identical to the existing
|
|
3087
|
+
# reference (e.g. ``SELECT col FROM t ORDER BY col``).
|
|
3088
|
+
if isinstance(match, exp.Column) and not match.table and match.name == column_name:
|
|
3089
|
+
return None
|
|
3090
|
+
|
|
3091
|
+
return match
|
|
3092
|
+
|
|
2958
3093
|
def ordered_sql(self, expression: exp.Ordered) -> str:
|
|
2959
3094
|
desc = expression.args.get("desc")
|
|
2960
3095
|
asc = not desc
|
|
@@ -3025,10 +3160,10 @@ class Generator:
|
|
|
3025
3160
|
f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
|
|
3026
3161
|
)
|
|
3027
3162
|
elif not isinstance(expression.this, exp.Rand):
|
|
3163
|
+
resolved = self._resolve_ordered_for_null_ordering_simulation(expression)
|
|
3164
|
+
target = self.sql(resolved) if resolved is not None else this
|
|
3028
3165
|
null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
|
|
3029
|
-
this =
|
|
3030
|
-
f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
|
|
3031
|
-
)
|
|
3166
|
+
this = f"CASE WHEN {target} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {target}"
|
|
3032
3167
|
nulls_sort_change = ""
|
|
3033
3168
|
|
|
3034
3169
|
with_fill = self.sql(expression, "with_fill")
|
|
@@ -3099,7 +3234,7 @@ class Generator:
|
|
|
3099
3234
|
*self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
|
|
3100
3235
|
*self.after_limit_modifiers(expression),
|
|
3101
3236
|
self.options_modifier(expression),
|
|
3102
|
-
self.
|
|
3237
|
+
self.sql(expression, "for_"),
|
|
3103
3238
|
sep="",
|
|
3104
3239
|
)
|
|
3105
3240
|
|
|
@@ -3107,9 +3242,16 @@ class Generator:
|
|
|
3107
3242
|
options = self.expressions(expression, key="options")
|
|
3108
3243
|
return f" {options}" if options else ""
|
|
3109
3244
|
|
|
3110
|
-
def
|
|
3111
|
-
|
|
3112
|
-
|
|
3245
|
+
def forclause_sql(self, expression: exp.ForClause) -> str:
|
|
3246
|
+
kind = expression.args["kind"]
|
|
3247
|
+
if kind == "BROWSE":
|
|
3248
|
+
return f"{self.sep()}FOR BROWSE"
|
|
3249
|
+
# FOR XML/JSON always carry at least AUTO/PATH. An empty rendering means
|
|
3250
|
+
# the target dialect doesn't support QueryOption, so we drop the clause.
|
|
3251
|
+
options = self.expressions(expression, key="expressions")
|
|
3252
|
+
if not options:
|
|
3253
|
+
return ""
|
|
3254
|
+
return f"{self.sep()}FOR {kind}{self.seg(options)}"
|
|
3113
3255
|
|
|
3114
3256
|
def queryoption_sql(self, expression: exp.QueryOption) -> str:
|
|
3115
3257
|
self.unsupported("Unsupported query option.")
|
|
@@ -3857,9 +3999,7 @@ class Generator:
|
|
|
3857
3999
|
else:
|
|
3858
4000
|
stack.append(expression.right)
|
|
3859
4001
|
if expression.comments and self.comments:
|
|
3860
|
-
|
|
3861
|
-
if comment:
|
|
3862
|
-
op += f" /*{self.sanitize_comment(comment)}*/"
|
|
4002
|
+
op = self.maybe_comment(op, comments=expression.comments)
|
|
3863
4003
|
stack.extend((op, expression.left))
|
|
3864
4004
|
return op
|
|
3865
4005
|
|
|
@@ -4215,6 +4355,9 @@ class Generator:
|
|
|
4215
4355
|
def distance_sql(self, expression: exp.Distance) -> str:
|
|
4216
4356
|
return self.binary(expression, "<->")
|
|
4217
4357
|
|
|
4358
|
+
def distancend_sql(self, expression: exp.DistanceNd) -> str:
|
|
4359
|
+
return self.binary(expression, "<<->>")
|
|
4360
|
+
|
|
4218
4361
|
def dot_sql(self, expression: exp.Dot) -> str:
|
|
4219
4362
|
return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
|
|
4220
4363
|
|
|
@@ -4536,6 +4679,15 @@ class Generator:
|
|
|
4536
4679
|
)
|
|
4537
4680
|
return f"{this}{expressions}" if expressions.strip() != "" else this
|
|
4538
4681
|
|
|
4682
|
+
def macrooverloads_sql(self, expression: exp.MacroOverloads) -> str:
|
|
4683
|
+
return self.expressions(expression, flat=True)
|
|
4684
|
+
|
|
4685
|
+
def macrooverload_sql(self, expression: exp.MacroOverload) -> str:
|
|
4686
|
+
params = self.no_identify(self.expressions, expression, flat=True)
|
|
4687
|
+
body = self.sql(expression, "this")
|
|
4688
|
+
prefix = "TABLE " if expression.args.get("is_table") else ""
|
|
4689
|
+
return f"({params}) AS {prefix}{body}"
|
|
4690
|
+
|
|
4539
4691
|
def joinhint_sql(self, expression: exp.JoinHint) -> str:
|
|
4540
4692
|
this = self.sql(expression, "this")
|
|
4541
4693
|
expressions = self.expressions(expression, flat=True)
|
|
@@ -4612,6 +4764,7 @@ class Generator:
|
|
|
4612
4764
|
def tochar_sql(self, expression: exp.ToChar) -> str:
|
|
4613
4765
|
return self.sql(exp.cast(expression.this, exp.DType.TEXT))
|
|
4614
4766
|
|
|
4767
|
+
@unsupported_args("default")
|
|
4615
4768
|
def tonumber_sql(self, expression: exp.ToNumber) -> str:
|
|
4616
4769
|
if not self.SUPPORTS_TO_NUMBER:
|
|
4617
4770
|
self.unsupported("Unsupported TO_NUMBER function")
|
|
@@ -688,3 +688,9 @@ class BigQueryGenerator(generator.Generator):
|
|
|
688
688
|
return f"{self.sql(expression, 'to')}{self.sql(this)}"
|
|
689
689
|
|
|
690
690
|
return super().cast_sql(expression, safe_prefix=safe_prefix)
|
|
691
|
+
|
|
692
|
+
def clusterproperty_sql(self, expression: exp.ClusterProperty) -> str:
|
|
693
|
+
if expression.this:
|
|
694
|
+
self.unsupported(f"Unsupported CLUSTER BY {self.sql(expression, 'this')}")
|
|
695
|
+
return ""
|
|
696
|
+
return self.op_expressions("CLUSTER BY", expression)
|
|
@@ -349,7 +349,7 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
349
349
|
exp.TimestampSub: _datetime_delta_sql("TIMESTAMP_SUB"),
|
|
350
350
|
exp.Typeof: rename_func("toTypeName"),
|
|
351
351
|
exp.VarMap: _map_sql,
|
|
352
|
-
exp.Xor: lambda self, e: self.func("xor", e.this, e.expression
|
|
352
|
+
exp.Xor: lambda self, e: self.func("xor", e.this, e.expression),
|
|
353
353
|
exp.MD5Digest: rename_func("MD5"),
|
|
354
354
|
exp.MD5: lambda self, e: self.func("LOWER", self.func("HEX", self.func("MD5", e.this))),
|
|
355
355
|
exp.SHA: rename_func("SHA1"),
|
|
@@ -109,3 +109,7 @@ class DatabricksGenerator(SparkGenerator):
|
|
|
109
109
|
seed = gen.this
|
|
110
110
|
|
|
111
111
|
return self.func("UNIFORM", expression.this, expression.expression, seed)
|
|
112
|
+
|
|
113
|
+
def clusterproperty_sql(self, expression):
|
|
114
|
+
this = self.sql(expression, "this") or f"({self.expressions(expression, flat=True)})"
|
|
115
|
+
return f"CLUSTER BY {this}"
|