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.
Files changed (112) hide show
  1. {sqlglotc-30.8.0/sqlglotc.egg-info → sqlglotc-30.10.0}/PKG-INFO +2 -2
  2. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/pyproject.toml +2 -2
  3. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/aggregate.py +1 -1
  4. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/core.py +33 -12
  5. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/datatypes.py +17 -5
  6. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/ddl.py +8 -0
  7. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/dml.py +1 -0
  8. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/functions.py +10 -1
  9. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/properties.py +4 -0
  10. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/query.py +12 -4
  11. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/string.py +1 -0
  12. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generator.py +166 -13
  13. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/bigquery.py +6 -0
  14. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/clickhouse.py +1 -1
  15. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/databricks.py +4 -0
  16. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/duckdb.py +60 -0
  17. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/exasol.py +6 -0
  18. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/mysql.py +37 -0
  19. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/oracle.py +10 -2
  20. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/presto.py +2 -6
  21. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/snowflake.py +0 -3
  22. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/sqlite.py +39 -10
  23. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/starrocks.py +5 -2
  24. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/tsql.py +18 -7
  25. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/annotate_types.py +10 -0
  26. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/qualify_columns.py +2 -2
  27. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/simplify.py +31 -3
  28. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parser.py +134 -23
  29. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/bigquery.py +29 -2
  30. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/clickhouse.py +9 -3
  31. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/databricks.py +5 -0
  32. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/duckdb.py +1 -0
  33. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/oracle.py +21 -0
  34. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/snowflake.py +16 -1
  35. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/spark2.py +1 -0
  36. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/tsql.py +72 -10
  37. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/serde.py +1 -1
  38. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/tokenizer_core.py +1 -0
  39. {sqlglotc-30.8.0 → sqlglotc-30.10.0/sqlglotc.egg-info}/PKG-INFO +2 -2
  40. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/requires.txt +1 -1
  41. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/MANIFEST.in +0 -0
  42. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/setup.cfg +0 -0
  43. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/setup.py +0 -0
  44. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/errors.py +0 -0
  45. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/executor/table.py +0 -0
  46. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/array.py +0 -0
  47. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/builders.py +0 -0
  48. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/constraints.py +0 -0
  49. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/json.py +0 -0
  50. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/math.py +0 -0
  51. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/expressions/temporal.py +0 -0
  52. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/athena.py +0 -0
  53. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/doris.py +0 -0
  54. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/dremio.py +0 -0
  55. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/drill.py +0 -0
  56. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/druid.py +0 -0
  57. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/dune.py +0 -0
  58. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/fabric.py +0 -0
  59. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/hive.py +0 -0
  60. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/materialize.py +0 -0
  61. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/postgres.py +0 -0
  62. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/prql.py +0 -0
  63. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/python.py +0 -0
  64. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/redshift.py +0 -0
  65. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/risingwave.py +0 -0
  66. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/singlestore.py +0 -0
  67. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/solr.py +0 -0
  68. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/spark.py +0 -0
  69. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/spark2.py +0 -0
  70. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/tableau.py +0 -0
  71. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/teradata.py +0 -0
  72. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/generators/trino.py +0 -0
  73. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/helper.py +0 -0
  74. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/lineage.py +0 -0
  75. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
  76. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  77. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  78. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/qualify.py +0 -0
  79. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  80. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/resolver.py +0 -0
  81. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/optimizer/scope.py +0 -0
  82. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/athena.py +0 -0
  83. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/base.py +0 -0
  84. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/doris.py +0 -0
  85. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/dremio.py +0 -0
  86. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/drill.py +0 -0
  87. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/druid.py +0 -0
  88. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/dune.py +0 -0
  89. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/exasol.py +0 -0
  90. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/fabric.py +0 -0
  91. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/hive.py +0 -0
  92. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/materialize.py +0 -0
  93. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/mysql.py +0 -0
  94. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/postgres.py +0 -0
  95. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/presto.py +0 -0
  96. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/prql.py +0 -0
  97. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/redshift.py +0 -0
  98. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/risingwave.py +0 -0
  99. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/singlestore.py +0 -0
  100. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/solr.py +0 -0
  101. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/spark.py +0 -0
  102. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/sqlite.py +0 -0
  103. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/starrocks.py +0 -0
  104. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/tableau.py +0 -0
  105. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/teradata.py +0 -0
  106. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/parsers/trino.py +0 -0
  107. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/schema.py +0 -0
  108. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/time.py +0 -0
  109. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglot/trie.py +0 -0
  110. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  111. {sqlglotc-30.8.0 → sqlglotc-30.10.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  112. {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.8.0
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.20.0.post6; extra == "dev"
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.20.0.post6"]
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.0.post6",
20
+ "sqlglot-mypy >= 2.1.0.post2",
21
21
  "types-python-dateutil",
22
22
  "sqlglot",
23
23
  ]
@@ -208,7 +208,7 @@ class NthValue(Expression, AggFunc):
208
208
 
209
209
 
210
210
  class ObjectAgg(Expression, AggFunc):
211
- arg_types = {"this": True, "expression": True}
211
+ arg_types = {"this": True, "expression": False}
212
212
 
213
213
 
214
214
  class PercentileCont(Expression, AggFunc):
@@ -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
- queue: deque[Expr] = deque()
841
- queue.append(self)
841
+ stack: list[Expr] = [self]
842
842
 
843
- while queue:
844
- node = queue.popleft()
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 child in node.iter_expressions():
848
- if child._hash is None:
849
- queue.append(child)
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, v in sorted(node.args.items()):
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, v in sorted(node.args.items()):
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": False, "expression": False, "expressions": False, "round_input": False}
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 type(node).__name__ != "DataType":
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: object,
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, dtype: str, dialect: DialectType = None, udt: bool = False, **kwargs: object
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
 
@@ -283,6 +283,7 @@ class LoadData(Expression):
283
283
  "this": True,
284
284
  "local": False,
285
285
  "overwrite": False,
286
+ "temp": False,
286
287
  "inpath": False,
287
288
  "files": False,
288
289
  "partition": False,
@@ -70,7 +70,12 @@ class Cast(Expression, Func):
70
70
 
71
71
 
72
72
  class TryCast(Cast):
73
- arg_types = {**Cast.arg_types, "requires_string": False}
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(Order):
863
- pass
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 DESC").sql(dialect="hive")
1270
- 'SELECT x FROM tbl CLUSTER BY x DESC'
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
@@ -375,6 +375,7 @@ class ToNumber(Expression, Func):
375
375
  "scale": False,
376
376
  "safe": False,
377
377
  "safe_name": False,
378
+ "default": False,
378
379
  }
379
380
 
380
381
 
@@ -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 self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
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
- return this
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
- this = f" {this}" if is_overwrite else f" INTO TABLE {this}"
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.for_modifiers(expression),
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 for_modifiers(self, expression: exp.Expr) -> str:
3111
- for_modifiers = self.expressions(expression, key="for_")
3112
- return f"{self.sep()}FOR XML{self.seg(for_modifiers)}" if for_modifiers else ""
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
- for comment in expression.comments:
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, *e.expressions),
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}"