sqlglotc 30.7.0__tar.gz → 30.8.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.7.0/sqlglotc.egg-info → sqlglotc-30.8.0}/PKG-INFO +1 -1
  2. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/array.py +1 -1
  3. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/core.py +2 -2
  4. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/datatypes.py +1 -0
  5. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/ddl.py +1 -1
  6. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/functions.py +8 -0
  7. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/query.py +21 -6
  8. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/string.py +1 -5
  9. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generator.py +81 -4
  10. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/bigquery.py +1 -0
  11. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/duckdb.py +12 -7
  12. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/exasol.py +21 -18
  13. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/hive.py +5 -4
  14. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/mysql.py +1 -0
  15. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/postgres.py +3 -0
  16. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/redshift.py +30 -0
  17. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/singlestore.py +6 -1
  18. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/snowflake.py +1 -0
  19. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/spark.py +8 -2
  20. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/tsql.py +1 -0
  21. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/annotate_types.py +111 -39
  22. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/qualify.py +3 -2
  23. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/qualify_columns.py +3 -3
  24. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/scope.py +13 -0
  25. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parser.py +86 -10
  26. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/bigquery.py +15 -14
  27. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/clickhouse.py +12 -5
  28. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/databricks.py +1 -3
  29. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/dremio.py +2 -2
  30. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/drill.py +1 -1
  31. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/duckdb.py +8 -3
  32. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/exasol.py +1 -1
  33. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/hive.py +26 -17
  34. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/mysql.py +13 -3
  35. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/oracle.py +5 -4
  36. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/postgres.py +7 -4
  37. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/presto.py +8 -4
  38. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/redshift.py +26 -1
  39. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/singlestore.py +3 -3
  40. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/snowflake.py +7 -5
  41. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/spark2.py +2 -2
  42. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/schema.py +19 -13
  43. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/serde.py +10 -6
  44. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/time.py +6 -5
  45. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/tokenizer_core.py +2 -0
  46. {sqlglotc-30.7.0 → sqlglotc-30.8.0/sqlglotc.egg-info}/PKG-INFO +1 -1
  47. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/MANIFEST.in +0 -0
  48. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/pyproject.toml +0 -0
  49. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/setup.cfg +0 -0
  50. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/setup.py +0 -0
  51. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/errors.py +0 -0
  52. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/executor/table.py +0 -0
  53. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/aggregate.py +0 -0
  54. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/builders.py +0 -0
  55. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/constraints.py +0 -0
  56. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/dml.py +0 -0
  57. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/json.py +0 -0
  58. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/math.py +0 -0
  59. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/properties.py +0 -0
  60. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/expressions/temporal.py +0 -0
  61. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/athena.py +0 -0
  62. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/clickhouse.py +0 -0
  63. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/databricks.py +0 -0
  64. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/doris.py +0 -0
  65. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/dremio.py +0 -0
  66. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/drill.py +0 -0
  67. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/druid.py +0 -0
  68. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/dune.py +0 -0
  69. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/fabric.py +0 -0
  70. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/materialize.py +0 -0
  71. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/oracle.py +0 -0
  72. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/presto.py +0 -0
  73. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/prql.py +0 -0
  74. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/python.py +0 -0
  75. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/risingwave.py +0 -0
  76. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/solr.py +0 -0
  77. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/spark2.py +0 -0
  78. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/sqlite.py +0 -0
  79. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/starrocks.py +0 -0
  80. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/tableau.py +0 -0
  81. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/teradata.py +0 -0
  82. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/generators/trino.py +0 -0
  83. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/helper.py +0 -0
  84. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/lineage.py +0 -0
  85. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
  86. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  87. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  88. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  89. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/resolver.py +0 -0
  90. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/optimizer/simplify.py +0 -0
  91. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/athena.py +0 -0
  92. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/base.py +0 -0
  93. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/doris.py +0 -0
  94. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/druid.py +0 -0
  95. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/dune.py +0 -0
  96. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/fabric.py +0 -0
  97. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/materialize.py +0 -0
  98. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/prql.py +0 -0
  99. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/risingwave.py +0 -0
  100. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/solr.py +0 -0
  101. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/spark.py +0 -0
  102. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/sqlite.py +0 -0
  103. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/starrocks.py +0 -0
  104. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/tableau.py +0 -0
  105. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/teradata.py +0 -0
  106. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/trino.py +0 -0
  107. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/parsers/tsql.py +0 -0
  108. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglot/trie.py +0 -0
  109. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  110. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  111. {sqlglotc-30.7.0 → sqlglotc-30.8.0}/sqlglotc.egg-info/requires.txt +0 -0
  112. {sqlglotc-30.7.0 → sqlglotc-30.8.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.7.0
3
+ Version: 30.8.0
4
4
  Summary: mypyc-compiled extensions for sqlglot
5
5
  Author-email: Toby Mao <toby.mao@gmail.com>
6
6
  License-Expression: MIT
@@ -365,5 +365,5 @@ class StDistance(Expression, Func):
365
365
 
366
366
 
367
367
  class StPoint(Expression, Func):
368
- arg_types = {"this": True, "expression": True, "null": False}
368
+ arg_types = {"this": True, "expression": True, "z": False, "m": False}
369
369
  _sql_names = ["ST_POINT", "ST_MAKEPOINT"]
@@ -2135,7 +2135,7 @@ class GTE(Expression, Binary, Predicate):
2135
2135
 
2136
2136
 
2137
2137
  class ILike(Expression, Binary, Predicate):
2138
- pass
2138
+ arg_types = {"this": True, "expression": True, "negate": False}
2139
2139
 
2140
2140
 
2141
2141
  class IntDiv(Expression, Binary):
@@ -2147,7 +2147,7 @@ class Is(Expression, Binary, Predicate):
2147
2147
 
2148
2148
 
2149
2149
  class Like(Expression, Binary, Predicate):
2150
- pass
2150
+ arg_types = {"this": True, "expression": True, "negate": False}
2151
2151
 
2152
2152
 
2153
2153
  class Match(Expression, Binary, Predicate):
@@ -178,6 +178,7 @@ class DataType(Expression):
178
178
  "values": False,
179
179
  "kind": False,
180
180
  "nullable": False,
181
+ "collate": False,
181
182
  }
182
183
 
183
184
  Type: t.ClassVar[Type[DType]] = DType
@@ -246,7 +246,7 @@ class AlterColumn(Expression):
246
246
 
247
247
 
248
248
  class ModifyColumn(Expression):
249
- pass
249
+ arg_types = {"this": True, "rename_from": False}
250
250
 
251
251
 
252
252
  class AlterIndex(Expression):
@@ -148,6 +148,10 @@ class Nullif(Expression, Func):
148
148
  arg_types = {"this": True, "expression": True}
149
149
 
150
150
 
151
+ class ObjectTransform(Expression, Func):
152
+ arg_types = {"this": True, "keep": False, "set_": False}
153
+
154
+
151
155
  class Nvl2(Expression, Func):
152
156
  arg_types = {"this": True, "true": True, "false": False}
153
157
 
@@ -301,6 +305,10 @@ class CurrentUser(Expression, Func):
301
305
  arg_types = {"this": False}
302
306
 
303
307
 
308
+ class CurrentUserId(Expression, Func):
309
+ arg_types = {}
310
+
311
+
304
312
  class CurrentVersion(Expression, Func):
305
313
  arg_types = {}
306
314
 
@@ -1763,14 +1763,15 @@ class Pivot(Expression):
1763
1763
  def fields(self) -> list[Expr]:
1764
1764
  return self.args.get("fields", [])
1765
1765
 
1766
- def output_columns(self, pre_pivot_columns: t.Iterable[str]) -> list[str]:
1766
+ def output_columns(self, pre_pivot_columns: t.Iterable[str]) -> dict[str, str]:
1767
1767
  """
1768
- Returns the columns produced by this (UN)PIVOT, in order.
1768
+ Returns an ordered map of post-rename output column name -> pre-rename
1769
+ source-side name, in the order the (UN)PIVOT produces them.
1769
1770
 
1770
- Example:
1771
+ For callers that just want the names, iterate the dict (or call .keys()):
1771
1772
  >>> from sqlglot import parse_one, exp
1772
1773
  >>> piv = parse_one("SELECT * FROM t UNPIVOT(val FOR name IN (a, b))").find(exp.Pivot)
1773
- >>> piv.output_columns(["a", "b", "c"])
1774
+ >>> list(piv.output_columns(["a", "b", "c"]))
1774
1775
  ['c', 'name', 'val']
1775
1776
 
1776
1777
  AST shape:
@@ -1813,9 +1814,23 @@ class Pivot(Expression):
1813
1814
  outputs = [c.alias_or_name for c in self.expressions]
1814
1815
 
1815
1816
  if not excluded or not outputs:
1816
- return []
1817
+ return {}
1818
+
1819
+ pre_rename = [c for c in pre_pivot_columns if c not in excluded] + outputs
1820
+
1821
+ alias = self.args.get("alias")
1822
+ renames = alias.args.get("columns") if alias else None
1823
+
1824
+ # `PIVOT(...) AS alias(c1, c2, ...)` renames the operator's output columns
1825
+ # positionally from the front (DuckDB, Snowflake): the user's names cover
1826
+ # the leading N output columns, remaining columns keep their auto names.
1827
+ if renames:
1828
+ rename_names = [r.name for r in renames]
1829
+ post_rename = rename_names + pre_rename[len(rename_names) :]
1830
+ else:
1831
+ post_rename = pre_rename
1817
1832
 
1818
- return [c for c in pre_pivot_columns if c not in excluded] + outputs
1833
+ return dict(zip(post_rename, pre_rename))
1819
1834
 
1820
1835
 
1821
1836
  class UnpivotColumns(Expression):
@@ -303,17 +303,13 @@ class FromBase64(Expression, Func):
303
303
 
304
304
 
305
305
  class Hex(Expression, Func):
306
- pass
306
+ arg_types = {"this": True, "case": False}
307
307
 
308
308
 
309
309
  class HexDecodeString(Expression, Func):
310
310
  pass
311
311
 
312
312
 
313
- class HexEncode(Expression, Func):
314
- arg_types = {"this": True, "case": False}
315
-
316
-
317
313
  class LowerHex(Hex):
318
314
  pass
319
315
 
@@ -446,6 +446,9 @@ class Generator:
446
446
  # Whether named columns are allowed in table aliases
447
447
  SUPPORTS_TABLE_ALIAS_COLUMNS = True
448
448
 
449
+ # Whether named columns are allowed in CTE definitions
450
+ SUPPORTS_NAMED_CTE_COLUMNS = True
451
+
449
452
  # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
450
453
  UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
451
454
 
@@ -467,6 +470,9 @@ class Generator:
467
470
  # Whether ALTER TABLE ... MODIFY COLUMN column-redefinition syntax is supported
468
471
  SUPPORTS_MODIFY_COLUMN = False
469
472
 
473
+ # Whether ALTER TABLE ... CHANGE COLUMN column-rename-and-redefine syntax is supported
474
+ SUPPORTS_CHANGE_COLUMN = False
475
+
470
476
  # Whether the LikeProperty needs to be specified inside of the schema clause
471
477
  LIKE_PROPERTY_INSIDE_SCHEMA = False
472
478
 
@@ -620,6 +626,11 @@ class Generator:
620
626
 
621
627
  UNSUPPORTED_TYPES: t.ClassVar[set[exp.DType]] = set()
622
628
 
629
+ # mapping of DType to its default parameters, bounds
630
+ TYPE_PARAM_SETTINGS: t.ClassVar[
631
+ dict[exp.DType, tuple[tuple[int, ...], tuple[int | None, ...]]]
632
+ ] = {}
633
+
623
634
  TIME_PART_SINGULARS: t.ClassVar = {
624
635
  "MICROSECONDS": "MICROSECOND",
625
636
  "SECONDS": "SECOND",
@@ -1526,7 +1537,11 @@ class Generator:
1526
1537
  columns = self.expressions(expression, key="columns", flat=True)
1527
1538
  columns = f"({columns})" if columns else ""
1528
1539
 
1529
- if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1540
+ if (
1541
+ columns
1542
+ and not self.SUPPORTS_TABLE_ALIAS_COLUMNS
1543
+ and not (self.SUPPORTS_NAMED_CTE_COLUMNS and isinstance(expression.parent, exp.CTE))
1544
+ ):
1530
1545
  columns = ""
1531
1546
  self.unsupported("Named columns are not supported in table alias.")
1532
1547
 
@@ -1629,11 +1644,59 @@ class Generator:
1629
1644
  specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1630
1645
  return f"{this}{specifier}"
1631
1646
 
1647
+ def datatype_param_bound_limiter(
1648
+ self,
1649
+ expression: exp.DataType,
1650
+ type_value: exp.DType,
1651
+ defaults: tuple[int, ...],
1652
+ bounds: tuple[int | None, ...],
1653
+ ) -> exp.DataType:
1654
+ params = expression.expressions
1655
+
1656
+ if not params:
1657
+ if defaults:
1658
+ expression.set(
1659
+ "expressions",
1660
+ [exp.DataTypeParam(this=exp.Literal.number(d)) for d in defaults],
1661
+ )
1662
+ return expression
1663
+
1664
+ if not bounds:
1665
+ return expression
1666
+
1667
+ for i, param in enumerate(params):
1668
+ bound = bounds[i] if i < len(bounds) else None
1669
+ if bound is None:
1670
+ continue
1671
+
1672
+ param_value = param.this if isinstance(param, exp.DataTypeParam) else param
1673
+ if (
1674
+ isinstance(param_value, exp.Literal)
1675
+ and param_value.is_number
1676
+ and int(param_value.to_py()) > bound
1677
+ ):
1678
+ self.unsupported(
1679
+ f"{type_value.value} parameter {param_value.name} exceeds "
1680
+ f"{self.dialect.__class__.__name__}'s maximum of {bound}; capping"
1681
+ )
1682
+ params[i] = exp.DataTypeParam(this=exp.Literal.number(bound))
1683
+
1684
+ return expression
1685
+
1632
1686
  def datatype_sql(self, expression: exp.DataType) -> str:
1633
1687
  nested = ""
1634
1688
  values = ""
1635
1689
 
1636
1690
  expr_nested = expression.args.get("nested")
1691
+ type_value = expression.this
1692
+
1693
+ if (
1694
+ not expr_nested
1695
+ and isinstance(type_value, exp.DType)
1696
+ and (settings := self.TYPE_PARAM_SETTINGS.get(type_value))
1697
+ ):
1698
+ expression = self.datatype_param_bound_limiter(expression, type_value, *settings)
1699
+
1637
1700
  interior = (
1638
1701
  self.expressions(
1639
1702
  expression, dynamic=True, new_line=True, skip_first=True, skip_last=True
@@ -1642,7 +1705,6 @@ class Generator:
1642
1705
  else self.expressions(expression, flat=True)
1643
1706
  )
1644
1707
 
1645
- type_value = expression.this
1646
1708
  if type_value in self.UNSUPPORTED_TYPES:
1647
1709
  self.unsupported(
1648
1710
  f"Data type {type_value.value} is not supported when targeting {self.dialect.__class__.__name__}"
@@ -1679,6 +1741,10 @@ class Generator:
1679
1741
  ):
1680
1742
  type_sql = f"{type_sql} WITH TIME ZONE"
1681
1743
 
1744
+ collate = self.sql(expression, "collate")
1745
+ if collate:
1746
+ type_sql = f"{type_sql} COLLATE {collate}"
1747
+
1682
1748
  return type_sql
1683
1749
 
1684
1750
  def directory_sql(self, expression: exp.Directory) -> str:
@@ -3944,9 +4010,15 @@ class Generator:
3944
4010
  return f"ALTER COLUMN {this} DROP DEFAULT"
3945
4011
 
3946
4012
  def modifycolumn_sql(self, expression: exp.ModifyColumn) -> str:
4013
+ this = self.sql(expression, "this")
4014
+ rename_from = self.sql(expression, "rename_from")
4015
+ if rename_from:
4016
+ if not self.SUPPORTS_CHANGE_COLUMN:
4017
+ self.unsupported("CHANGE COLUMN is not supported in this dialect")
4018
+ return f"CHANGE COLUMN {rename_from} {this}"
3947
4019
  if not self.SUPPORTS_MODIFY_COLUMN:
3948
4020
  self.unsupported("MODIFY COLUMN is not supported in this dialect")
3949
- return f"MODIFY COLUMN {self.sql(expression, 'this')}"
4021
+ return f"MODIFY COLUMN {this}"
3950
4022
 
3951
4023
  def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3952
4024
  this = self.sql(expression, "this")
@@ -4193,6 +4265,9 @@ class Generator:
4193
4265
  exp_class = exp.ILike
4194
4266
  op = "ILIKE"
4195
4267
 
4268
+ if expression.args.get("negate"):
4269
+ op = f"NOT {op}"
4270
+
4196
4271
  if isinstance(rhs, (exp.All, exp.Any)) and not self.SUPPORTS_LIKE_QUANTIFIERS:
4197
4272
  exprs = rhs.this.unnest()
4198
4273
 
@@ -4204,7 +4279,9 @@ class Generator:
4204
4279
  connective = exp.or_ if isinstance(rhs, exp.Any) else exp.and_
4205
4280
 
4206
4281
  def _make_like(expr: exp.Expression) -> exp.Expression:
4207
- like: exp.Expression = exp_class(this=this, expression=expr)
4282
+ like: exp.Expression = exp_class(
4283
+ this=this, expression=expr, negate=expression.args.get("negate")
4284
+ )
4208
4285
  if escape:
4209
4286
  like = exp.Escape(this=like, expression=escape.expression.copy())
4210
4287
  return like
@@ -232,6 +232,7 @@ class BigQueryGenerator(generator.Generator):
232
232
  COLLATE_IS_FUNC = True
233
233
  LIMIT_ONLY_LITERALS = True
234
234
  SUPPORTS_TABLE_ALIAS_COLUMNS = False
235
+ SUPPORTS_NAMED_CTE_COLUMNS = False
235
236
  UNPIVOT_ALIASES_ARE_IDENTIFIERS = False
236
237
  JSON_KEY_VALUE_PAIR_SEP = ","
237
238
  NULL_ORDERING_SUPPORTED: bool | None = False
@@ -1581,6 +1581,7 @@ class DuckDBGenerator(generator.Generator):
1581
1581
  f"CAST(STRFTIME({self.sql(e, 'this')}, {self.dialect.DATEINT_FORMAT}) AS INT)"
1582
1582
  ),
1583
1583
  exp.Decode: lambda self, e: encode_decode_sql(self, e, "DECODE", replace=False),
1584
+ exp.HexDecodeString: lambda self, e: self.sql(exp.Decode(this=exp.Unhex(this=e.this))),
1584
1585
  exp.DiToDate: lambda self, e: (
1585
1586
  f"CAST(STRPTIME(CAST({self.sql(e, 'this')} AS TEXT), {self.dialect.DATEINT_FORMAT}) AS DATE)"
1586
1587
  ),
@@ -1727,7 +1728,7 @@ class DuckDBGenerator(generator.Generator):
1727
1728
  exp.DType.BPCHAR: "TEXT",
1728
1729
  exp.DType.CHAR: "TEXT",
1729
1730
  exp.DType.DATETIME: "TIMESTAMP",
1730
- exp.DType.DECFLOAT: "DECIMAL(38, 5)",
1731
+ exp.DType.DECFLOAT: "DECIMAL",
1731
1732
  exp.DType.FLOAT: "REAL",
1732
1733
  exp.DType.JSONB: "JSON",
1733
1734
  exp.DType.NCHAR: "TEXT",
@@ -1741,7 +1742,13 @@ class DuckDBGenerator(generator.Generator):
1741
1742
  exp.DType.TIMESTAMP_S: "TIMESTAMP_S",
1742
1743
  exp.DType.TIMESTAMP_MS: "TIMESTAMP_MS",
1743
1744
  exp.DType.TIMESTAMP_NS: "TIMESTAMP_NS",
1744
- exp.DType.BIGDECIMAL: "DECIMAL(38, 5)",
1745
+ exp.DType.BIGDECIMAL: "DECIMAL",
1746
+ }
1747
+
1748
+ TYPE_PARAM_SETTINGS = {
1749
+ **generator.Generator.TYPE_PARAM_SETTINGS,
1750
+ exp.DType.BIGDECIMAL: ((38, 5), (38, 38)),
1751
+ exp.DType.DECFLOAT: ((38, 5), (38, 38)),
1745
1752
  }
1746
1753
 
1747
1754
  # https://github.com/duckdb/duckdb/blob/ff7f24fd8e3128d94371827523dae85ebaf58713/third_party/libpg_query/grammar/keywords/reserved_keywords.list#L1-L77
@@ -3607,15 +3614,13 @@ class DuckDBGenerator(generator.Generator):
3607
3614
 
3608
3615
  return self.sql(result)
3609
3616
 
3610
- def hexencode_sql(self, expression: exp.HexEncode) -> str:
3611
- arg = expression.this
3617
+ def hex_sql(self, expression: exp.Hex) -> str:
3612
3618
  case = expression.args.get("case")
3613
- hex_expr = exp.Hex(this=arg)
3614
3619
 
3615
3620
  if not case:
3616
- return self.sql(hex_expr)
3621
+ return self.func("HEX", expression.this)
3617
3622
 
3618
- # Emit runtime CASE WHEN to handle NULL propagation and case=0 (lowercase) vs case=1 (uppercase)
3623
+ hex_expr = exp.Hex(this=expression.this)
3619
3624
  return self.sql(
3620
3625
  exp.case()
3621
3626
  .when(case.is_(exp.null()), exp.null())
@@ -2,9 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import typing as t
4
4
 
5
- from sqlglot import exp, generator, transforms
5
+ from sqlglot import exp, generator
6
6
  from sqlglot.dialects.dialect import (
7
7
  DATE_ADD_OR_SUB,
8
+ Dialect,
8
9
  groupconcat_sql,
9
10
  no_last_day_sql,
10
11
  rename_func,
@@ -35,10 +36,13 @@ def _date_diff_sql(self: ExasolGenerator, expression: exp.DateDiff | exp.TsOrDsD
35
36
 
36
37
 
37
38
  # https://docs.exasol.com/db/latest/sql/select.htm#:~:text=If%20you%20have,local.x%3E10
38
- def _add_local_prefix_for_aliases(expression: exp.Expr) -> exp.Expr:
39
+ def _add_local_prefix_for_aliases(expression: exp.Expr, dialect: Dialect) -> exp.Expr:
40
+ def _key(ident: exp.Identifier) -> str:
41
+ return dialect.normalize_identifier(ident.copy()).this
42
+
39
43
  if isinstance(expression, exp.Select):
40
- aliases: dict[str, bool] = {
41
- alias.name: bool(alias.args.get("quoted"))
44
+ aliases: dict[str, exp.Identifier] = {
45
+ _key(alias): alias
42
46
  for sel in expression.selects
43
47
  if isinstance(sel, exp.Alias) and (alias := sel.args.get("alias"))
44
48
  }
@@ -53,11 +57,12 @@ def _add_local_prefix_for_aliases(expression: exp.Expr) -> exp.Expr:
53
57
  ):
54
58
  table_ident.replace(exp.to_identifier(table_ident.name.upper(), quoted=True))
55
59
 
56
- def prefix_local(node: exp.Expr, visible_aliases: dict[str, bool]) -> exp.Expr:
60
+ def prefix_local(node: exp.Expr, visible_aliases: dict[str, exp.Identifier]) -> exp.Expr:
57
61
  if isinstance(node, exp.Column) and not node.table:
58
- if node.name in visible_aliases:
62
+ alias = visible_aliases.get(_key(node.this))
63
+ if alias is not None:
59
64
  return exp.Column(
60
- this=exp.to_identifier(node.name, quoted=visible_aliases[node.name]),
65
+ this=alias,
61
66
  table=exp.to_identifier("LOCAL", quoted=False),
62
67
  )
63
68
  return node
@@ -66,16 +71,15 @@ def _add_local_prefix_for_aliases(expression: exp.Expr) -> exp.Expr:
66
71
  if arg := expression.args.get(key):
67
72
  expression.set(key, arg.transform(lambda node: prefix_local(node, aliases)))
68
73
 
69
- seen_aliases: dict[str, bool] = {}
74
+ seen_aliases: dict[str, exp.Identifier] = {}
70
75
  new_selects: list[exp.Expr] = []
71
76
  for sel in expression.selects:
72
77
  if isinstance(sel, exp.Alias):
73
78
  inner = sel.this.transform(lambda node: prefix_local(node, seen_aliases))
74
79
  sel.set("this", inner)
75
80
 
76
- alias_node = sel.args.get("alias")
77
-
78
- seen_aliases[sel.alias] = bool(alias_node and getattr(alias_node, "quoted", False))
81
+ if alias_node := sel.args.get("alias"):
82
+ seen_aliases[_key(alias_node)] = alias_node
79
83
  new_selects.append(sel)
80
84
  else:
81
85
  new_selects.append(sel.transform(lambda node: prefix_local(node, seen_aliases)))
@@ -303,6 +307,12 @@ class ExasolGenerator(generator.Generator):
303
307
  exp.DType.TIMESTAMPNTZ: "TIMESTAMP",
304
308
  }
305
309
 
310
+ def select_sql(self, expression: exp.Select) -> str:
311
+ processed = _qualify_unscoped_star(expression)
312
+ processed = _add_local_prefix_for_aliases(processed, self.dialect)
313
+ processed = _group_by_all(processed)
314
+ return super().select_sql(t.cast(exp.Select, processed))
315
+
306
316
  def datatype_sql(self, expression: exp.DataType) -> str:
307
317
  # Exasol supports a fixed default precision of 3 for TIMESTAMP WITH LOCAL TIME ZONE
308
318
  # and does not allow specifying a different custom precision
@@ -390,13 +400,6 @@ class ExasolGenerator(generator.Generator):
390
400
  exp.MD5Digest: rename_func("HASHTYPE_MD5"),
391
401
  # https://docs.exasol.com/db/latest/sql/create_view.htm
392
402
  exp.CommentColumnConstraint: lambda self, e: f"COMMENT IS {self.sql(e, 'this')}",
393
- exp.Select: transforms.preprocess(
394
- [
395
- _qualify_unscoped_star,
396
- _add_local_prefix_for_aliases,
397
- _group_by_all,
398
- ]
399
- ),
400
403
  exp.SubstringIndex: _substring_index_sql,
401
404
  exp.WeekOfYear: rename_func("WEEK"),
402
405
  # https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/to_date.htm
@@ -433,17 +433,18 @@ class HiveGenerator(generator.Generator):
433
433
  if expression.this in self.PARAMETERIZABLE_TEXT_TYPES and (
434
434
  not expression.expressions or expression.expressions[0].name == "MAX"
435
435
  ):
436
- expression = exp.DType.TEXT.into_expr()
436
+ expression.set("this", exp.DType.TEXT)
437
+ expression.set("expressions", None)
437
438
  elif expression.is_type(exp.DType.TEXT) and expression.expressions:
438
439
  expression.set("this", exp.DType.VARCHAR)
439
440
  elif expression.this in exp.DataType.TEMPORAL_TYPES:
440
- expression = exp.DataType.build(expression.this)
441
+ expression.set("expressions", None)
441
442
  elif expression.is_type("float"):
442
443
  size_expression = expression.find(exp.DataTypeParam)
443
444
  if size_expression:
444
445
  size = int(size_expression.name)
445
- builder = exp.DType.FLOAT if size <= 32 else exp.DType.DOUBLE
446
- expression = builder.into_expr()
446
+ expression.set("this", exp.DType.FLOAT if size <= 32 else exp.DType.DOUBLE)
447
+ expression.set("expressions", None)
447
448
  return super().datatype_sql(expression)
448
449
 
449
450
  def version_sql(self, expression: exp.Version) -> str:
@@ -112,6 +112,7 @@ class MySQLGenerator(generator.Generator):
112
112
  SUPPORTS_UESCAPE = False
113
113
  SUPPORTS_DECODE_CASE = False
114
114
  SUPPORTS_MODIFY_COLUMN = True
115
+ SUPPORTS_CHANGE_COLUMN = True
115
116
 
116
117
  AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
117
118
 
@@ -481,6 +481,9 @@ class PostgresGenerator(generator.Generator):
481
481
  return f"{self.expressions(expression, flat=True)}[{values}]"
482
482
  return "ARRAY"
483
483
 
484
+ if expression.is_type(exp.DType.ENUM):
485
+ return f"ENUM ({self.expressions(expression, flat=True)})"
486
+
484
487
  if expression.is_type(exp.DType.DOUBLE, exp.DType.FLOAT) and expression.expressions:
485
488
  # Postgres doesn't support precision for REAL and DOUBLE PRECISION types
486
489
  return f"FLOAT({self.expressions(expression, flat=True)})"
@@ -67,6 +67,7 @@ class RedshiftGenerator(PostgresGenerator):
67
67
  exp.SHA2,
68
68
  exp.Getbit,
69
69
  exp.Round,
70
+ exp.TryCast,
70
71
  }
71
72
  },
72
73
  exp.ArrayConcat: array_concat_sql("ARRAY_CONCAT"),
@@ -74,6 +75,7 @@ class RedshiftGenerator(PostgresGenerator):
74
75
  exp.ConcatWs: concat_ws_to_dpipe_sql,
75
76
  exp.ApproxDistinct: lambda self, e: f"APPROXIMATE COUNT(DISTINCT {self.sql(e, 'this')})",
76
77
  exp.CurrentTimestamp: lambda self, e: "SYSDATE" if e.args.get("sysdate") else "GETDATE()",
78
+ exp.CurrentUserId: lambda *_: "CURRENT_USER_ID",
77
79
  exp.DateAdd: date_delta_sql("DATEADD"),
78
80
  exp.DateDiff: date_delta_sql("DATEDIFF"),
79
81
  exp.DistKeyProperty: lambda self, e: self.func("DISTKEY", e.this),
@@ -270,6 +272,34 @@ class RedshiftGenerator(PostgresGenerator):
270
272
  "without",
271
273
  }
272
274
 
275
+ def stpoint_sql(self, expression: exp.StPoint) -> str:
276
+ # ST_POINT only accepts 2 args in Redshift; use ST_MAKEPOINT for 3 or 4 args
277
+ if expression.args.get("z") or expression.args.get("m"):
278
+ return self.func(
279
+ "ST_MAKEPOINT",
280
+ expression.this,
281
+ expression.expression,
282
+ expression.args.get("z"),
283
+ expression.args.get("m"),
284
+ )
285
+ return self.func("ST_POINT", expression.this, expression.expression)
286
+
287
+ def arraycontains_sql(self, expression: exp.ArrayContains) -> str:
288
+ return self.func(
289
+ "ARRAY_CONTAINS",
290
+ expression.this,
291
+ expression.expression,
292
+ expression.args.get("check_null"),
293
+ )
294
+
295
+ def objecttransform_sql(self, expression: exp.ObjectTransform) -> str:
296
+ this = self.sql(expression, "this")
297
+ keep = self.expressions(expression, key="keep", flat=True)
298
+ set_ = self.expressions(expression, key="set_", flat=True)
299
+ keep_sql = " KEEP " + keep if keep else ""
300
+ set_sql = " SET " + set_ if set_ else ""
301
+ return f"OBJECT_TRANSFORM({this}{keep_sql}{set_sql})"
302
+
273
303
  def approxquantile_sql(self, expression: exp.ApproxQuantile) -> str:
274
304
  return "APPROXIMATE " + self.sql(
275
305
  exp.WithinGroup(
@@ -361,7 +361,12 @@ class SingleStoreGenerator(MySQLGenerator):
361
361
  exp.DType.JSONB: "BSON",
362
362
  exp.DType.TIMESTAMP: "TIMESTAMP",
363
363
  exp.DType.TIMESTAMP_S: "TIMESTAMP",
364
- exp.DType.TIMESTAMP_MS: "TIMESTAMP(6)",
364
+ exp.DType.TIMESTAMP_MS: "TIMESTAMP",
365
+ }
366
+
367
+ TYPE_PARAM_SETTINGS = {
368
+ **MySQLGenerator.TYPE_PARAM_SETTINGS,
369
+ exp.DType.TIMESTAMP_MS: ((6,), ()),
365
370
  }
366
371
 
367
372
  # https://docs.singlestore.com/cloud/reference/sql-reference/restricted-keywords/list-of-restricted-keywords/
@@ -559,6 +559,7 @@ class SnowflakeGenerator(generator.Generator):
559
559
  exp.MD5Digest: rename_func("MD5_BINARY"),
560
560
  exp.MD5NumberLower64: rename_func("MD5_NUMBER_LOWER64"),
561
561
  exp.MD5NumberUpper64: rename_func("MD5_NUMBER_UPPER64"),
562
+ exp.Hex: rename_func("HEX_ENCODE"),
562
563
  exp.LowerHex: rename_func("TO_CHAR"),
563
564
  exp.Skewness: rename_func("SKEW"),
564
565
  exp.StarMap: rename_func("OBJECT_CONSTRUCT"),
@@ -76,13 +76,19 @@ class SparkGenerator(Spark2Generator):
76
76
 
77
77
  TYPE_MAPPING = {
78
78
  **Spark2Generator.TYPE_MAPPING,
79
- exp.DType.MONEY: "DECIMAL(15, 4)",
80
- exp.DType.SMALLMONEY: "DECIMAL(6, 4)",
79
+ exp.DType.MONEY: "DECIMAL",
80
+ exp.DType.SMALLMONEY: "DECIMAL",
81
81
  exp.DType.UUID: "STRING",
82
82
  exp.DType.TIMESTAMPLTZ: "TIMESTAMP_LTZ",
83
83
  exp.DType.TIMESTAMPNTZ: "TIMESTAMP_NTZ",
84
84
  }
85
85
 
86
+ TYPE_PARAM_SETTINGS = {
87
+ **Spark2Generator.TYPE_PARAM_SETTINGS,
88
+ exp.DType.MONEY: ((15, 4), ()),
89
+ exp.DType.SMALLMONEY: ((6, 4), ()),
90
+ }
91
+
86
92
  TRANSFORMS = {
87
93
  k: v
88
94
  for k, v in {
@@ -234,6 +234,7 @@ class TSQLGenerator(generator.Generator):
234
234
  exp.TemporaryProperty: lambda self, e: "",
235
235
  exp.TimeStrToTime: _timestrtotime_sql,
236
236
  exp.TimeToStr: _format_sql,
237
+ exp.TimestampAdd: date_delta_sql("DATEADD"),
237
238
  exp.Trim: trim_sql,
238
239
  exp.TsOrDsAdd: date_delta_sql("DATEADD", cast=True),
239
240
  exp.TsOrDsDiff: date_delta_sql("DATEDIFF"),