sqlglotc 30.8.0__tar.gz → 30.9.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.9.0}/PKG-INFO +2 -2
  2. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/pyproject.toml +2 -2
  3. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/aggregate.py +1 -1
  4. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/core.py +33 -12
  5. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/datatypes.py +17 -5
  6. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/ddl.py +8 -0
  7. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/dml.py +1 -0
  8. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/functions.py +5 -1
  9. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/query.py +8 -0
  10. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/string.py +1 -0
  11. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generator.py +158 -13
  12. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/clickhouse.py +1 -1
  13. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/duckdb.py +34 -0
  14. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/exasol.py +6 -0
  15. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/mysql.py +37 -0
  16. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/oracle.py +10 -2
  17. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/sqlite.py +39 -10
  18. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/tsql.py +18 -7
  19. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/annotate_types.py +10 -0
  20. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/simplify.py +31 -3
  21. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parser.py +121 -14
  22. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/bigquery.py +22 -2
  23. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/clickhouse.py +8 -3
  24. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/duckdb.py +1 -0
  25. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/oracle.py +21 -0
  26. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/snowflake.py +14 -0
  27. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/spark2.py +1 -0
  28. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/tsql.py +72 -10
  29. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/serde.py +1 -1
  30. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/tokenizer_core.py +1 -0
  31. {sqlglotc-30.8.0 → sqlglotc-30.9.0/sqlglotc.egg-info}/PKG-INFO +2 -2
  32. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglotc.egg-info/requires.txt +1 -1
  33. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/MANIFEST.in +0 -0
  34. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/setup.cfg +0 -0
  35. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/setup.py +0 -0
  36. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/errors.py +0 -0
  37. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/executor/table.py +0 -0
  38. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/array.py +0 -0
  39. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/builders.py +0 -0
  40. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/constraints.py +0 -0
  41. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/json.py +0 -0
  42. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/math.py +0 -0
  43. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/properties.py +0 -0
  44. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/expressions/temporal.py +0 -0
  45. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/athena.py +0 -0
  46. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/bigquery.py +0 -0
  47. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/databricks.py +0 -0
  48. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/doris.py +0 -0
  49. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/dremio.py +0 -0
  50. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/drill.py +0 -0
  51. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/druid.py +0 -0
  52. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/dune.py +0 -0
  53. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/fabric.py +0 -0
  54. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/hive.py +0 -0
  55. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/materialize.py +0 -0
  56. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/postgres.py +0 -0
  57. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/presto.py +0 -0
  58. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/prql.py +0 -0
  59. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/python.py +0 -0
  60. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/redshift.py +0 -0
  61. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/risingwave.py +0 -0
  62. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/singlestore.py +0 -0
  63. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/snowflake.py +0 -0
  64. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/solr.py +0 -0
  65. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/spark.py +0 -0
  66. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/spark2.py +0 -0
  67. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/starrocks.py +0 -0
  68. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/tableau.py +0 -0
  69. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/teradata.py +0 -0
  70. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/generators/trino.py +0 -0
  71. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/helper.py +0 -0
  72. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/lineage.py +0 -0
  73. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
  74. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  75. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  76. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/qualify.py +0 -0
  77. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/qualify_columns.py +0 -0
  78. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  79. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/resolver.py +0 -0
  80. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/optimizer/scope.py +0 -0
  81. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/athena.py +0 -0
  82. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/base.py +0 -0
  83. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/databricks.py +0 -0
  84. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/doris.py +0 -0
  85. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/dremio.py +0 -0
  86. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/drill.py +0 -0
  87. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/druid.py +0 -0
  88. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/dune.py +0 -0
  89. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/exasol.py +0 -0
  90. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/fabric.py +0 -0
  91. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/hive.py +0 -0
  92. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/materialize.py +0 -0
  93. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/mysql.py +0 -0
  94. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/postgres.py +0 -0
  95. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/presto.py +0 -0
  96. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/prql.py +0 -0
  97. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/redshift.py +0 -0
  98. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/risingwave.py +0 -0
  99. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/singlestore.py +0 -0
  100. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/solr.py +0 -0
  101. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/spark.py +0 -0
  102. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/sqlite.py +0 -0
  103. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/starrocks.py +0 -0
  104. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/tableau.py +0 -0
  105. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/teradata.py +0 -0
  106. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/parsers/trino.py +0 -0
  107. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/schema.py +0 -0
  108. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/time.py +0 -0
  109. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglot/trie.py +0 -0
  110. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  111. {sqlglotc-30.8.0 → sqlglotc-30.9.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  112. {sqlglotc-30.8.0 → sqlglotc-30.9.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.9.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,7 @@ 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 = {**Cast.arg_types, "requires_string": False, "null_on_text_overflow": False}
74
74
 
75
75
 
76
76
  class JSONCast(Cast):
@@ -436,6 +436,10 @@ class XMLElement(Expression, Func):
436
436
  arg_types = {"this": True, "expressions": False, "evalname": False}
437
437
 
438
438
 
439
+ class GetIgnoreCase(Expression, Func):
440
+ arg_types = {"this": True, "expression": True}
441
+
442
+
439
443
  class XMLGet(Expression, Func):
440
444
  _sql_names = ["XMLGET"]
441
445
  arg_types = {"this": True, "expression": True, "instance": False}
@@ -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
 
@@ -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
 
@@ -1328,7 +1328,9 @@ class Generator:
1328
1328
  if expression_sql:
1329
1329
  expression_sql = f"{begin}{self.sep()}{expression_sql}"
1330
1330
 
1331
- if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1331
+ if not isinstance(expression.expression, exp.MacroOverloads) and (
1332
+ self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return)
1333
+ ):
1332
1334
  postalias_props_sql = ""
1333
1335
  if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1334
1336
  postalias_props_sql = self.properties(
@@ -1605,7 +1607,12 @@ class Generator:
1605
1607
  )
1606
1608
 
1607
1609
  return delimited_byte_string
1608
- return this
1610
+
1611
+ if "\\" in self.dialect.tokenizer_class.STRING_ESCAPES:
1612
+ return self.sql(exp.Literal.string(this))
1613
+
1614
+ self.unsupported(f"Byte strings are not supported for {self.dialect.__class__.__name__}")
1615
+ return ""
1609
1616
 
1610
1617
  def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1611
1618
  this = self.sql(expression, "this")
@@ -2454,6 +2461,81 @@ class Generator:
2454
2461
 
2455
2462
  return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
2456
2463
 
2464
+ def _pivot_in_value_aliases(self, expression: exp.Pivot) -> list[exp.Expression] | None:
2465
+ # Returns the rewritten field.expressions list with PivotAlias wrappers injected where
2466
+ # the stored column name differs from the target dialect's natural output.
2467
+ columns = expression.args.get("columns")
2468
+ if not columns or len(expression.fields) != 1:
2469
+ return None
2470
+
2471
+ args = expression.args
2472
+ parser_cls = self.dialect.parser_class
2473
+
2474
+ tgt_identify_pivot_strings = parser_cls.IDENTIFY_PIVOT_STRINGS
2475
+ tgt_prefixed_pivot_columns = parser_cls.PREFIXED_PIVOT_COLUMNS
2476
+ tgt_pivot_column_naming = parser_cls.PIVOT_COLUMN_NAMING
2477
+
2478
+ src_identify_pivot_strings = args.get("identify_pivot_strings", tgt_identify_pivot_strings)
2479
+ src_prefixed_pivot_columns = args.get("prefixed_pivot_columns", tgt_prefixed_pivot_columns)
2480
+ src_pivot_column_naming = args.get("pivot_column_naming", tgt_pivot_column_naming)
2481
+
2482
+ if (
2483
+ src_identify_pivot_strings == tgt_identify_pivot_strings
2484
+ and src_prefixed_pivot_columns == tgt_prefixed_pivot_columns
2485
+ and src_pivot_column_naming == tgt_pivot_column_naming
2486
+ ):
2487
+ return None
2488
+
2489
+ in_exprs = expression.fields[0].expressions
2490
+ step = len(columns) // len(in_exprs)
2491
+
2492
+ # Derive the per-value suffix from the first stored column vs the first IN-list value.
2493
+ # This correctly handles dialects (e.g. Spark single-agg) that ignore agg aliases.
2494
+ first_base = in_exprs[0].sql() if src_identify_pivot_strings else in_exprs[0].alias_or_name
2495
+ first_stored = columns[0].name
2496
+
2497
+ # exit if only suffix matches, not prefix. (e.g. BigQuery, which cannot be fixed)
2498
+ if not first_stored.startswith(first_base):
2499
+ return None
2500
+
2501
+ suffix = first_stored[len(first_base) :]
2502
+
2503
+ # Whether the target dialect would append an agg-name suffix for this pivot.
2504
+ # Spark single-agg uniquely drops the agg alias entirely.
2505
+ target_has_suffix = (
2506
+ len(expression.expressions) > 1 or tgt_pivot_column_naming != "agg_name_if_multiple"
2507
+ ) and any(a.alias for a in expression.expressions)
2508
+ source_has_suffix = suffix != ""
2509
+
2510
+ new_exprs: list[exp.Expression] = []
2511
+ modified = False
2512
+ for val_idx, e in enumerate(in_exprs):
2513
+ if isinstance(e, exp.PivotAlias):
2514
+ new_exprs.append(e)
2515
+ continue
2516
+
2517
+ i = val_idx * step
2518
+ stored_full = columns[i].name
2519
+ stored_value = stored_full[: -len(suffix)] if suffix else stored_full
2520
+ target_value = e.sql() if tgt_identify_pivot_strings else e.alias_or_name
2521
+
2522
+ # Source had a suffix, but target won't apply one
2523
+ if source_has_suffix and not target_has_suffix:
2524
+ new_exprs.append(
2525
+ exp.PivotAlias(this=e, alias=exp.to_identifier(stored_full, quoted=True))
2526
+ )
2527
+ modified = True
2528
+ # Value-part mismatch (e.g. Snowflake's literal-style values vs others).
2529
+ elif stored_value != target_value:
2530
+ new_exprs.append(
2531
+ exp.PivotAlias(this=e, alias=exp.to_identifier(stored_value, quoted=True))
2532
+ )
2533
+ modified = True
2534
+ else:
2535
+ new_exprs.append(e)
2536
+
2537
+ return new_exprs if modified else None
2538
+
2457
2539
  def pivot_sql(self, expression: exp.Pivot) -> str:
2458
2540
  expressions = self.expressions(expression, flat=True)
2459
2541
  direction = "UNPIVOT" if expression.unpivot else "PIVOT"
@@ -2473,6 +2555,12 @@ class Generator:
2473
2555
  sql = f"{direction} {this}{on}{into}{using}{group}"
2474
2556
  return self.prepend_ctes(expression, sql)
2475
2557
 
2558
+ if not expression.unpivot:
2559
+ # Wrap IN-list values with explicit aliases where the target dialect would differ
2560
+ new_field_exprs = self._pivot_in_value_aliases(expression)
2561
+ if new_field_exprs is not None:
2562
+ expression.fields[0].set("expressions", new_field_exprs)
2563
+
2476
2564
  alias = self.sql(expression, "alias")
2477
2565
  alias = f" AS {alias}" if alias else ""
2478
2566
 
@@ -2896,7 +2984,12 @@ class Generator:
2896
2984
  if files:
2897
2985
  files_sql = self.expressions(files, flat=True)
2898
2986
  files_sql = f"FILES{self.wrap(files_sql)}"
2899
- this = f" {this}" if is_overwrite else f" INTO TABLE {this}"
2987
+ if is_overwrite:
2988
+ this = f" {this}"
2989
+ elif expression.args.get("temp"):
2990
+ this = f" INTO TEMP TABLE {this}"
2991
+ else:
2992
+ this = f" INTO TABLE {this}"
2900
2993
  return f"LOAD DATA{overwrite}{this} FROM {files_sql}"
2901
2994
 
2902
2995
  local = " LOCAL" if expression.args.get("local") else ""
@@ -2955,6 +3048,40 @@ class Generator:
2955
3048
  def sort_sql(self, expression: exp.Sort) -> str:
2956
3049
  return self.op_expressions("SORT BY", expression)
2957
3050
 
3051
+ def _resolve_ordered_for_null_ordering_simulation(
3052
+ self, expression: exp.Ordered
3053
+ ) -> exp.Expr | None:
3054
+ """Resolve a bare ORDER BY name against the enclosing SELECT projection.
3055
+
3056
+ Returns the underlying expression of the uniquely-matching projection
3057
+ (Alias-stripped) for substitution into the NULLS FIRST/LAST CASE
3058
+ simulation, since the CASE is evaluated in FROM-clause scope rather
3059
+ than alias scope (MySQL error 1052). Returns None if no safe
3060
+ substitution applies, leaving the original behaviour unchanged.
3061
+ """
3062
+ this = expression.this
3063
+ if not (isinstance(this, exp.Column) and not this.table):
3064
+ return None
3065
+
3066
+ ancestor = expression.find_ancestor(exp.Select, exp.Window)
3067
+ if not isinstance(ancestor, exp.Select):
3068
+ return None
3069
+
3070
+ column_name = this.name
3071
+ matched: list[exp.Expr] = [
3072
+ p.this if isinstance(p, exp.Alias) else p
3073
+ for p in ancestor.selects
3074
+ if p.output_name == column_name
3075
+ ]
3076
+ match = matched[0] if len(matched) == 1 else None
3077
+
3078
+ # Skip the substitution when it would be identical to the existing
3079
+ # reference (e.g. ``SELECT col FROM t ORDER BY col``).
3080
+ if isinstance(match, exp.Column) and not match.table and match.name == column_name:
3081
+ return None
3082
+
3083
+ return match
3084
+
2958
3085
  def ordered_sql(self, expression: exp.Ordered) -> str:
2959
3086
  desc = expression.args.get("desc")
2960
3087
  asc = not desc
@@ -3025,10 +3152,10 @@ class Generator:
3025
3152
  f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
3026
3153
  )
3027
3154
  elif not isinstance(expression.this, exp.Rand):
3155
+ resolved = self._resolve_ordered_for_null_ordering_simulation(expression)
3156
+ target = self.sql(resolved) if resolved is not None else this
3028
3157
  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
- )
3158
+ this = f"CASE WHEN {target} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {target}"
3032
3159
  nulls_sort_change = ""
3033
3160
 
3034
3161
  with_fill = self.sql(expression, "with_fill")
@@ -3099,7 +3226,7 @@ class Generator:
3099
3226
  *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
3100
3227
  *self.after_limit_modifiers(expression),
3101
3228
  self.options_modifier(expression),
3102
- self.for_modifiers(expression),
3229
+ self.sql(expression, "for_"),
3103
3230
  sep="",
3104
3231
  )
3105
3232
 
@@ -3107,9 +3234,16 @@ class Generator:
3107
3234
  options = self.expressions(expression, key="options")
3108
3235
  return f" {options}" if options else ""
3109
3236
 
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 ""
3237
+ def forclause_sql(self, expression: exp.ForClause) -> str:
3238
+ kind = expression.args["kind"]
3239
+ if kind == "BROWSE":
3240
+ return f"{self.sep()}FOR BROWSE"
3241
+ # FOR XML/JSON always carry at least AUTO/PATH. An empty rendering means
3242
+ # the target dialect doesn't support QueryOption, so we drop the clause.
3243
+ options = self.expressions(expression, key="expressions")
3244
+ if not options:
3245
+ return ""
3246
+ return f"{self.sep()}FOR {kind}{self.seg(options)}"
3113
3247
 
3114
3248
  def queryoption_sql(self, expression: exp.QueryOption) -> str:
3115
3249
  self.unsupported("Unsupported query option.")
@@ -3857,9 +3991,7 @@ class Generator:
3857
3991
  else:
3858
3992
  stack.append(expression.right)
3859
3993
  if expression.comments and self.comments:
3860
- for comment in expression.comments:
3861
- if comment:
3862
- op += f" /*{self.sanitize_comment(comment)}*/"
3994
+ op = self.maybe_comment(op, comments=expression.comments)
3863
3995
  stack.extend((op, expression.left))
3864
3996
  return op
3865
3997
 
@@ -4215,6 +4347,9 @@ class Generator:
4215
4347
  def distance_sql(self, expression: exp.Distance) -> str:
4216
4348
  return self.binary(expression, "<->")
4217
4349
 
4350
+ def distancend_sql(self, expression: exp.DistanceNd) -> str:
4351
+ return self.binary(expression, "<<->>")
4352
+
4218
4353
  def dot_sql(self, expression: exp.Dot) -> str:
4219
4354
  return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
4220
4355
 
@@ -4536,6 +4671,15 @@ class Generator:
4536
4671
  )
4537
4672
  return f"{this}{expressions}" if expressions.strip() != "" else this
4538
4673
 
4674
+ def macrooverloads_sql(self, expression: exp.MacroOverloads) -> str:
4675
+ return self.expressions(expression, flat=True)
4676
+
4677
+ def macrooverload_sql(self, expression: exp.MacroOverload) -> str:
4678
+ params = self.no_identify(self.expressions, expression, flat=True)
4679
+ body = self.sql(expression, "this")
4680
+ prefix = "TABLE " if expression.args.get("is_table") else ""
4681
+ return f"({params}) AS {prefix}{body}"
4682
+
4539
4683
  def joinhint_sql(self, expression: exp.JoinHint) -> str:
4540
4684
  this = self.sql(expression, "this")
4541
4685
  expressions = self.expressions(expression, flat=True)
@@ -4612,6 +4756,7 @@ class Generator:
4612
4756
  def tochar_sql(self, expression: exp.ToChar) -> str:
4613
4757
  return self.sql(exp.cast(expression.this, exp.DType.TEXT))
4614
4758
 
4759
+ @unsupported_args("default")
4615
4760
  def tonumber_sql(self, expression: exp.ToNumber) -> str:
4616
4761
  if not self.SUPPORTS_TO_NUMBER:
4617
4762
  self.unsupported("Unsupported TO_NUMBER function")
@@ -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"),
@@ -2285,6 +2285,10 @@ class DuckDBGenerator(generator.Generator):
2285
2285
  f"({self.sql(exp.replace_placeholders(self.BITMAP_CONSTRUCT_AGG_TEMPLATE, arg=arg))})"
2286
2286
  )
2287
2287
 
2288
+ def getignorecase_sql(self, expression: exp.GetIgnoreCase) -> str:
2289
+ self.unsupported("DuckDB does not support the GET_IGNORE_CASE() function")
2290
+ return self.function_fallback_sql(expression)
2291
+
2288
2292
  def compress_sql(self, expression: exp.Compress) -> str:
2289
2293
  self.unsupported("DuckDB does not support the COMPRESS() function")
2290
2294
  return self.function_fallback_sql(expression)
@@ -2647,6 +2651,15 @@ class DuckDBGenerator(generator.Generator):
2647
2651
  )
2648
2652
  )
2649
2653
 
2654
+ def parsetime_sql(self, expression: exp.ParseTime) -> str:
2655
+ formatted_time = self.format_time(expression)
2656
+ return self.sql(
2657
+ exp.cast(
2658
+ self.func("STRPTIME", expression.this, formatted_time),
2659
+ exp.DataType(this=exp.DType.TIME),
2660
+ )
2661
+ )
2662
+
2650
2663
  def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
2651
2664
  this = expression.this
2652
2665
  time_format = self.format_time(expression)
@@ -4313,6 +4326,27 @@ class DuckDBGenerator(generator.Generator):
4313
4326
 
4314
4327
  return self.func(func, this, decimals, truncate)
4315
4328
 
4329
+ def trycast_sql(self, expression: exp.TryCast) -> str:
4330
+ to = expression.to
4331
+ to_type = to.this
4332
+
4333
+ if (
4334
+ expression.args.get("null_on_text_overflow")
4335
+ and to_type in exp.DataType.TEXT_TYPES
4336
+ and to.expressions
4337
+ ):
4338
+ src = expression.this
4339
+ return self.sql(
4340
+ exp.case()
4341
+ .when(
4342
+ exp.LTE(this=exp.func("LENGTH", src), expression=to.expressions[0].this),
4343
+ exp.cast(src, "TEXT"),
4344
+ )
4345
+ .else_(exp.Null())
4346
+ )
4347
+
4348
+ return super().trycast_sql(expression)
4349
+
4316
4350
  def strtok_sql(self, expression: exp.Strtok) -> str:
4317
4351
  string_arg = expression.this
4318
4352
  delimiter_arg = expression.args.get("delimiter")
@@ -916,6 +916,12 @@ class ExasolGenerator(generator.Generator):
916
916
  )
917
917
  return self.sql(select)
918
918
 
919
+ if expression.name == "DATABASES":
920
+ select = exp.select(exp.column("SCHEMA_NAME")).from_(
921
+ exp.table_("EXA_SCHEMAS", db="SYS")
922
+ )
923
+ return self.sql(select)
924
+
919
925
  return super().show_sql(expression)
920
926
 
921
927
  def collate_sql(self, expression: exp.Collate) -> str: