sqlglotc 30.24.0__tar.gz → 30.26.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 (118) hide show
  1. {sqlglotc-30.24.0/sqlglotc.egg-info → sqlglotc-30.26.0}/PKG-INFO +3 -3
  2. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/pyproject.toml +3 -2
  3. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/array.py +4 -0
  4. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/builders.py +1 -1
  5. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/constraints.py +2 -1
  6. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/core.py +11 -8
  7. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/datatypes.py +3 -1
  8. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/ddl.py +8 -0
  9. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/properties.py +4 -0
  10. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/query.py +8 -3
  11. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generator.py +133 -24
  12. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/bigquery.py +1 -0
  13. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/clickhouse.py +7 -7
  14. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/databricks.py +1 -0
  15. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/duckdb.py +111 -14
  16. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/exasol.py +3 -6
  17. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/hive.py +4 -1
  18. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/oracle.py +0 -3
  19. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/postgres.py +67 -11
  20. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/presto.py +7 -2
  21. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/python.py +5 -0
  22. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/snowflake.py +3 -0
  23. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/sqlite.py +6 -2
  24. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/tsql.py +5 -1
  25. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/annotate_types.py +239 -176
  26. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/canonicalize_internal_names.py +11 -10
  27. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/qualify_columns.py +94 -158
  28. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/resolver.py +60 -9
  29. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/scope.py +23 -4
  30. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/simplify.py +5 -1
  31. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parser.py +102 -27
  32. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/clickhouse.py +6 -9
  33. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/duckdb.py +95 -3
  34. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/mysql.py +8 -0
  35. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/oracle.py +18 -0
  36. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/postgres.py +56 -5
  37. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/snowflake.py +37 -16
  38. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/spark.py +1 -1
  39. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/sqlite.py +32 -1
  40. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/tokenizer_core.py +2 -0
  41. {sqlglotc-30.24.0 → sqlglotc-30.26.0/sqlglotc.egg-info}/PKG-INFO +3 -3
  42. sqlglotc-30.26.0/sqlglotc.egg-info/requires.txt +6 -0
  43. sqlglotc-30.26.0/sqlglotc.egg-info/scm_version.json +8 -0
  44. sqlglotc-30.24.0/sqlglotc.egg-info/requires.txt +0 -6
  45. sqlglotc-30.24.0/sqlglotc.egg-info/scm_version.json +0 -8
  46. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/MANIFEST.in +0 -0
  47. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/setup.cfg +0 -0
  48. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/setup.py +0 -0
  49. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/anonymize.py +0 -0
  50. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/errors.py +0 -0
  51. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/executor/table.py +0 -0
  52. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/aggregate.py +0 -0
  53. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/dml.py +0 -0
  54. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/functions.py +0 -0
  55. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/json.py +0 -0
  56. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/math.py +0 -0
  57. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/string.py +0 -0
  58. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/temporal.py +0 -0
  59. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/athena.py +0 -0
  60. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/dax.py +0 -0
  61. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/doris.py +0 -0
  62. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/dremio.py +0 -0
  63. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/drill.py +0 -0
  64. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/druid.py +0 -0
  65. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/dune.py +0 -0
  66. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/fabric.py +0 -0
  67. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/materialize.py +0 -0
  68. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/mysql.py +0 -0
  69. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/prql.py +0 -0
  70. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/redshift.py +0 -0
  71. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/risingwave.py +0 -0
  72. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/singlestore.py +0 -0
  73. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/solr.py +0 -0
  74. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/spark.py +0 -0
  75. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/spark2.py +0 -0
  76. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/starrocks.py +0 -0
  77. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/tableau.py +0 -0
  78. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/teradata.py +0 -0
  79. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/trino.py +0 -0
  80. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/helper.py +0 -0
  81. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/lineage.py +0 -0
  82. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  83. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  84. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/qualify.py +0 -0
  85. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  86. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/athena.py +0 -0
  87. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/base.py +0 -0
  88. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/bigquery.py +0 -0
  89. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/databricks.py +0 -0
  90. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/dax.py +0 -0
  91. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/doris.py +0 -0
  92. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/dremio.py +0 -0
  93. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/drill.py +0 -0
  94. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/druid.py +0 -0
  95. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/dune.py +0 -0
  96. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/exasol.py +0 -0
  97. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/fabric.py +0 -0
  98. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/hive.py +0 -0
  99. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/materialize.py +0 -0
  100. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/presto.py +0 -0
  101. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/prql.py +0 -0
  102. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/redshift.py +0 -0
  103. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/risingwave.py +0 -0
  104. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/singlestore.py +0 -0
  105. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/solr.py +0 -0
  106. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/spark2.py +0 -0
  107. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/starrocks.py +0 -0
  108. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/tableau.py +0 -0
  109. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/teradata.py +0 -0
  110. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/trino.py +0 -0
  111. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/tsql.py +0 -0
  112. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/serde.py +0 -0
  113. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/time.py +0 -0
  114. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/trie.py +0 -0
  115. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  116. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  117. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
  118. {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/top_level.txt +0 -0
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlglotc
3
- Version: 30.24.0
3
+ Version: 30.26.0
4
4
  Summary: mypyc-compiled extensions for sqlglot
5
5
  Author-email: Toby Mao <toby.mao@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://sqlglot.com/
8
8
  Project-URL: Repository, https://github.com/tobymao/sqlglot
9
9
  Requires-Python: >=3.10
10
- Requires-Dist: sqlglot==30.24.0
10
+ Requires-Dist: sqlglot==30.26.0
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: setuptools>=61.0; extra == "dev"
13
- Requires-Dist: setuptools_scm; extra == "dev"
13
+ Requires-Dist: setuptools_scm>=10.2; extra == "dev"
14
14
  Requires-Dist: mypy>=2.4.0; extra == "dev"
15
15
  Dynamic: requires-dist
@@ -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", "mypy >= 2.4.0"]
10
+ dev = ["setuptools >= 61.0", "setuptools_scm >= 10.2", "mypy >= 2.4.0"]
11
11
 
12
12
  [project.urls]
13
13
  Homepage = "https://sqlglot.com/"
@@ -16,7 +16,7 @@ Repository = "https://github.com/tobymao/sqlglot"
16
16
  [build-system]
17
17
  requires = [
18
18
  "setuptools >= 61.0",
19
- "setuptools_scm",
19
+ "setuptools_scm >= 10.2",
20
20
  "mypy >= 2.4.0",
21
21
  "types-python-dateutil",
22
22
  "sqlglot",
@@ -28,6 +28,7 @@ include-package-data = false
28
28
 
29
29
  [tool.setuptools_scm]
30
30
  root = ".."
31
+ write_to_source = true
31
32
  fallback_version = "0.0.0"
32
33
  local_scheme = "no-local-version"
33
34
 
@@ -254,6 +254,10 @@ class SplitToTable(Expression, Func, UDTF):
254
254
  arg_types = {"this": True, "expression": True}
255
255
 
256
256
 
257
+ class StrtokSplitToTable(Expression, Func, UDTF):
258
+ arg_types = {"this": True, "expression": False}
259
+
260
+
257
261
  class Stack(Expression, Func, UDTF):
258
262
  arg_types = {"this": True, "expressions": True}
259
263
  is_var_len_args = True
@@ -921,7 +921,7 @@ def replace_placeholders(expression: Expr, *args: object, **kwargs: t.Any) -> Ex
921
921
  def _replace_placeholders(node: Expr, args: Iterator[object], **kwargs: object) -> Expr:
922
922
  if isinstance(node, Placeholder):
923
923
  if node.this:
924
- new_name = kwargs.get(node.this)
924
+ new_name = kwargs.get(node.name)
925
925
  if new_name is not None:
926
926
  return _replace_placeholder(node, convert(new_name))
927
927
  else:
@@ -161,7 +161,7 @@ class OnUpdateColumnConstraint(Expression, ColumnConstraintKind):
161
161
 
162
162
 
163
163
  class PrimaryKeyColumnConstraint(Expression, ColumnConstraintKind):
164
- arg_types = {"desc": False, "options": False}
164
+ arg_types = {"desc": False, "options": False, "include": False}
165
165
 
166
166
 
167
167
  class TitleColumnConstraint(Expression, ColumnConstraintKind):
@@ -175,6 +175,7 @@ class UniqueColumnConstraint(Expression, ColumnConstraintKind):
175
175
  "on_conflict": False,
176
176
  "nulls": False,
177
177
  "options": False,
178
+ "include": False,
178
179
  }
179
180
 
180
181
 
@@ -1734,13 +1734,13 @@ class Column(Expression, Condition):
1734
1734
  return self.name
1735
1735
 
1736
1736
  @property
1737
- def parts(self) -> list[Identifier | Star]:
1737
+ def parts(self) -> list[Expr]:
1738
1738
  """Return the parts of a column in order catalog, db, table, name."""
1739
1739
  return [
1740
1740
  self.args[part] for part in ("catalog", "db", "table", "this") if self.args.get(part)
1741
1741
  ]
1742
1742
 
1743
- def to_dot(self, include_dots: bool = True) -> Dot | Identifier | Star:
1743
+ def to_dot(self, include_dots: bool = True) -> Expr:
1744
1744
  """Converts the column into a dot expression."""
1745
1745
  parts = self.parts
1746
1746
  parent = self.parent
@@ -2108,6 +2108,10 @@ class Filter(Expression):
2108
2108
  arg_types = {"this": True, "expression": True}
2109
2109
 
2110
2110
 
2111
+ class Per(Expression):
2112
+ arg_types = {"this": True, "expressions": True}
2113
+
2114
+
2111
2115
  class Check(Expression):
2112
2116
  pass
2113
2117
 
@@ -3071,7 +3075,7 @@ def alias_(
3071
3075
 
3072
3076
  @t.overload
3073
3077
  def column(
3074
- col: str | Identifier,
3078
+ col: str | Expr,
3075
3079
  table: str | Identifier | None = None,
3076
3080
  db: str | Identifier | None = None,
3077
3081
  catalog: str | Identifier | None = None,
@@ -3085,7 +3089,7 @@ def column(
3085
3089
 
3086
3090
  @t.overload
3087
3091
  def column(
3088
- col: str | Identifier | Star,
3092
+ col: str | Expr,
3089
3093
  table: str | Identifier | None = None,
3090
3094
  db: str | Identifier | None = None,
3091
3095
  catalog: str | Identifier | None = None,
@@ -3111,19 +3115,18 @@ def column(
3111
3115
  Build a Column.
3112
3116
 
3113
3117
  Args:
3114
- col: Column name.
3118
+ col: Column name or expression.
3115
3119
  table: Table name.
3116
3120
  db: Database name.
3117
3121
  catalog: Catalog name.
3118
3122
  fields: Additional fields using dots.
3119
3123
  quoted: Whether to force quotes on the column's identifiers.
3120
- copy: Whether to copy identifiers if passed in.
3124
+ copy: Whether to copy expressions if passed in.
3121
3125
 
3122
3126
  Returns:
3123
3127
  The new Column instance.
3124
3128
  """
3125
- if not isinstance(col, Star):
3126
- col = to_identifier(col, quoted=quoted, copy=copy)
3129
+ col = maybe_copy(col, copy) if isinstance(col, Expr) else to_identifier(col, quoted=quoted)
3127
3130
 
3128
3131
  this: Column | Dot = Column(
3129
3132
  this=col,
@@ -461,7 +461,9 @@ class IntervalSpan(DataType):
461
461
 
462
462
 
463
463
  class Interval(_TimeUnit):
464
- arg_types = {"this": False, "unit": False}
464
+ # fraction_in_string: a fractional amount was written with its unit in one string, e.g.
465
+ # INTERVAL '1.5 DAY', which differs from INTERVAL '1.5' DAY in e.g. DuckDB and Postgres
466
+ arg_types = {"this": False, "unit": False, "fraction_in_string": False}
465
467
 
466
468
 
467
469
  DATA_TYPE = t.Union[str, Identifier, Dot, DataType, DType]
@@ -131,6 +131,14 @@ class Describe(Expression):
131
131
  }
132
132
 
133
133
 
134
+ class ConnectTo(Expression):
135
+ pass
136
+
137
+
138
+ class Disconnect(Expression):
139
+ arg_types = {}
140
+
141
+
134
142
  class Attach(Expression):
135
143
  arg_types = {"this": True, "exists": False, "expressions": False}
136
144
 
@@ -107,6 +107,10 @@ class CopyGrantsProperty(Property):
107
107
  arg_types = {}
108
108
 
109
109
 
110
+ class CopyTagsProperty(Property):
111
+ arg_types = {}
112
+
113
+
110
114
  class DataBlocksizeProperty(Property):
111
115
  arg_types = {
112
116
  "size": False,
@@ -663,7 +663,7 @@ class GroupingSets(Expression):
663
663
 
664
664
 
665
665
  class Lambda(Expression):
666
- arg_types = {"this": True, "expressions": True, "colon": False}
666
+ arg_types = {"this": True, "expressions": True}
667
667
 
668
668
 
669
669
  class Limit(Expression):
@@ -699,8 +699,9 @@ class Join(Expression):
699
699
  "method": False,
700
700
  "global_": False,
701
701
  "hint": False,
702
- "match_condition": False, # Snowflake
703
- "directed": False, # Snowflake
702
+ "match_condition": False,
703
+ "nearest": False,
704
+ "directed": False,
704
705
  "expressions": False,
705
706
  "pivots": False,
706
707
  }
@@ -817,6 +818,10 @@ class Join(Expression):
817
818
  return join
818
819
 
819
820
 
821
+ class Nearest(Expression):
822
+ arg_types = {"this": True, "count": True, "approximate": False, "similarity": False}
823
+
824
+
820
825
  class Lateral(Expression, UDTF):
821
826
  arg_types = {
822
827
  "this": True,
@@ -169,6 +169,7 @@ class Generator:
169
169
  "CONVERT", e.this, e.args["dest"], e.args.get("source")
170
170
  ),
171
171
  exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
172
+ exp.CopyTagsProperty: lambda *_: "COPY TAGS",
172
173
  exp.CredentialsProperty: lambda self, e: (
173
174
  f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})"
174
175
  ),
@@ -333,6 +334,9 @@ class Generator:
333
334
  # Whether MERGE ... WHEN MATCHED/NOT MATCHED THEN UPDATE/INSERT ... WHERE is supported
334
335
  SUPPORTS_MERGE_WHERE = False
335
336
 
337
+ # Whether MERGE uses ALL BY NAME instead of a star to update or insert all columns
338
+ MERGE_ALL_BY_NAME = False
339
+
336
340
  # Whether the INTERVAL expression works only with values like '1 day'
337
341
  SINGLE_STRING_INTERVAL = False
338
342
 
@@ -370,6 +374,9 @@ class Generator:
370
374
  # Whether directed joins are supported
371
375
  DIRECTED_JOINS = False
372
376
 
377
+ # Whether top-k distance/similarity joins are supported
378
+ SUPPORTS_NEAREST_JOIN = False
379
+
373
380
  # Whether table hints should be generated
374
381
  TABLE_HINTS = True
375
382
 
@@ -414,9 +421,12 @@ class Generator:
414
421
  # SELECT * VALUES into SELECT UNION
415
422
  VALUES_AS_TABLE = True
416
423
 
417
- # Whether the word COLUMN is included when adding a column with ALTER TABLE
424
+ # Whether COLUMN/COLUMNS keywords are included when adding columns with ALTER TABLE.
418
425
  ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
419
426
 
427
+ # Whether ALTER TABLE mutli-column additions are wrapped in parentheses.
428
+ ALTER_TABLE_ADD_COLUMNS_WRAPPED = True
429
+
420
430
  # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
421
431
  UNNEST_WITH_ORDINALITY = True
422
432
 
@@ -722,6 +732,7 @@ class Generator:
722
732
  exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
723
733
  exp.ComputeProperty: exp.Properties.Location.POST_CREATE,
724
734
  exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
735
+ exp.CopyTagsProperty: exp.Properties.Location.POST_SCHEMA,
725
736
  exp.Cluster: exp.Properties.Location.POST_SCHEMA,
726
737
  exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
727
738
  exp.ClusterProperty: exp.Properties.Location.POST_SCHEMA,
@@ -1322,7 +1333,8 @@ class Generator:
1322
1333
  return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1323
1334
  options = self.expressions(expression, key="options", flat=True, sep=" ")
1324
1335
  options = f" {options}" if options else ""
1325
- return f"PRIMARY KEY{options}"
1336
+ include = self.sql(expression, "include")
1337
+ return f"PRIMARY KEY{include}{options}"
1326
1338
 
1327
1339
  def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1328
1340
  this = self.sql(expression, "this")
@@ -1334,7 +1346,8 @@ class Generator:
1334
1346
  nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1335
1347
  options = self.expressions(expression, key="options", flat=True, sep=" ")
1336
1348
  options = f" {options}" if options else ""
1337
- return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
1349
+ include = self.sql(expression, "include")
1350
+ return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{include}{options}"
1338
1351
 
1339
1352
  def inoutcolumnconstraint_sql(self, expression: exp.InOutColumnConstraint) -> str:
1340
1353
  input_ = expression.args.get("input_")
@@ -1953,19 +1966,63 @@ class Generator:
1953
1966
  return self.sql(select)
1954
1967
 
1955
1968
  sqls: list[str] = []
1956
- stack: list[str | exp.Expr] = [expression]
1969
+ stack: list[str | exp.Expr | int | None] = [expression]
1970
+ depth = 0
1971
+ open_parens = ""
1957
1972
 
1958
1973
  while stack:
1959
1974
  node = stack.pop()
1960
1975
 
1961
- if isinstance(node, exp.SetOperation):
1976
+ if isinstance(node, int):
1977
+ # Closes a plain grouping opened below, restoring its depth
1978
+ depth = node
1979
+ sqls[-1] += self.sep("") + self.indent(")", level=depth, pad=0)
1980
+ elif isinstance(node, exp.SetOperation):
1962
1981
  stack.append(node.expression)
1963
1982
  stack.append(
1964
1983
  self.maybe_comment(
1965
1984
  self.set_operation(node), comments=node.comments, separated=True
1966
1985
  )
1967
1986
  )
1968
- stack.append(node.this)
1987
+ left = node.this
1988
+ if (
1989
+ not self.SET_OP_PARENTHESIZED_OPERANDS
1990
+ and not self.dialect.INTERSECT_BINDS_TIGHTER_THAN_UNION_AND_EXCEPT
1991
+ ):
1992
+ # Left grouping is implicit, so skip the `SELECT * FROM (...)` wrapper:
1993
+ # it renames duplicate columns and deepens recursion. Inner CTEs and
1994
+ # modifiers still need the wrapper.
1995
+ wrappers: list[exp.Subquery] = []
1996
+ inner = left
1997
+ while isinstance(inner, exp.Subquery) and inner.is_wrapper:
1998
+ wrappers.append(inner)
1999
+ inner = inner.this
2000
+
2001
+ if (
2002
+ isinstance(inner, exp.SetOperation)
2003
+ and not inner.args.get("with_")
2004
+ and not any(inner.args.get(key) for key in exp.QUERY_MODIFIERS)
2005
+ ):
2006
+ for wrapper in wrappers:
2007
+ inner.add_comments(wrapper.pop_comments())
2008
+ left = inner
2009
+ elif (
2010
+ self.SET_OP_PARENTHESIZED_OPERANDS
2011
+ and isinstance(left, exp.Subquery)
2012
+ and left.is_wrapper
2013
+ and not left.comments
2014
+ and isinstance(left.this, exp.SetOperation)
2015
+ and not left.this.args.get("with_")
2016
+ and not any(left.this.args.get(key) for key in exp.QUERY_MODIFIERS)
2017
+ ):
2018
+ # Write a plain grouping's parentheses here rather than recursing through
2019
+ # subquery_sql: left-to-right chains nest one grouping per INTERSECT, and
2020
+ # long ones would exhaust the stack. The depth pushed here marks the close.
2021
+ open_parens += self.indent("(", level=depth, pad=0) + self.sep("")
2022
+ stack.append(depth)
2023
+ depth += 1
2024
+ left = left.this
2025
+ stack.append(left)
1969
2026
  else:
1970
2027
  if (
1971
2028
  not self.SET_OP_LIMITS
@@ -1975,7 +2032,14 @@ class Generator:
1975
2032
  node = node.subquery(copy=False)
1976
2033
  if not self.SET_OP_PARENTHESIZED_OPERANDS:
1977
2034
  node = exp.select("*").from_(node, copy=False)
1978
- sqls.append(self.sql(node))
2035
+ elif (
2036
+ not self.SET_OP_PARENTHESIZED_OPERANDS
2037
+ and isinstance(node, exp.Subquery)
2038
+ and isinstance(node.unnest(), exp.SetOperation)
2039
+ ):
2040
+ node = exp.select("*").from_(node, copy=False)
2041
+ sqls.append(open_parens + self.indent(self.sql(node), level=depth, pad=0))
2042
+ open_parens = ""
1979
2043
 
1980
2044
  this = self.sep().join(sqls)
1981
2045
  this = self.query_modifiers(expression, this)
@@ -2003,6 +2067,9 @@ class Generator:
2003
2067
  where = self.sql(expression, "expression").strip()
2004
2068
  return f"{this} FILTER({where})"
2005
2069
 
2070
+ def per_sql(self, expression: exp.Per) -> str:
2071
+ return f"{self.sql(expression, 'this')} PER ({self.expressions(expression, flat=True)})"
2072
+
2006
2073
  def hint_sql(self, expression: exp.Hint) -> str:
2007
2074
  if not self.QUERY_HINTS:
2008
2075
  self.unsupported("Hints are not supported")
@@ -2024,7 +2091,9 @@ class Generator:
2024
2091
  with_storage = self.expressions(expression, key="with_storage", flat=True)
2025
2092
  with_storage = f" WITH ({with_storage})" if with_storage else ""
2026
2093
  tablespace = self.sql(expression, "tablespace")
2027
- tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
2094
+ if tablespace:
2095
+ prefix = "" if isinstance(expression.parent, exp.Index) else " USING INDEX"
2096
+ tablespace = f"{prefix} TABLESPACE {tablespace}"
2028
2097
  on = self.sql(expression, "on")
2029
2098
  on = f" ON {on}" if on else ""
2030
2099
 
@@ -2168,7 +2237,7 @@ class Generator:
2168
2237
  def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
2169
2238
  if isinstance(expression.this, exp.Dot):
2170
2239
  return self.sql(expression, "this")
2171
- return f"'{expression.name}'" if string_key else expression.name
2240
+ return self.sql(exp.Literal.string(expression.name)) if string_key else expression.name
2172
2241
 
2173
2242
  def property_sql(self, expression: exp.Property) -> str:
2174
2243
  property_cls = expression.__class__
@@ -2911,6 +2980,12 @@ class Generator:
2911
2980
  this = self.indent(self.sql(expression, "this"))
2912
2981
  return f"{self.seg('HAVING')}{self.sep()}{this}"
2913
2982
 
2983
+ def connectto_sql(self, expression: exp.ConnectTo) -> str:
2984
+ return f"CONNECT {self.sql(expression, 'this')}"
2985
+
2986
+ def disconnect_sql(self, expression: exp.Disconnect) -> str:
2987
+ return "DISCONNECT"
2988
+
2914
2989
  def connect_sql(self, expression: exp.Connect) -> str:
2915
2990
  start = self.sql(expression, "start")
2916
2991
  start = self.seg(f"START WITH {start}") if start else ""
@@ -2922,7 +2997,7 @@ class Generator:
2922
2997
  def prior_sql(self, expression: exp.Prior) -> str:
2923
2998
  return f"PRIOR {self.sql(expression, 'this')}"
2924
2999
 
2925
- def join_sql(self, expression: exp.Join) -> str:
3000
+ def join_sql(self, expression: exp.Join, join_op: str | None = None) -> str:
2926
3001
  if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2927
3002
  side = None
2928
3003
  else:
@@ -2942,6 +3017,8 @@ class Generator:
2942
3017
  )
2943
3018
  match_cond = self.sql(expression, "match_condition")
2944
3019
  match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
3020
+ nearest = self.sql(expression, "nearest")
3021
+ nearest = f" {nearest}" if nearest else ""
2945
3022
  on_sql = self.sql(expression, "on")
2946
3023
  using = expression.args.get("using")
2947
3024
 
@@ -2962,17 +3039,27 @@ class Generator:
2962
3039
  on_sql = f"{space}USING ({on_sql})"
2963
3040
  else:
2964
3041
  on_sql = f"{space}ON {on_sql}"
2965
- elif not op_sql:
3042
+ elif not op_sql and not nearest:
2966
3043
  if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2967
3044
  return f" {this_sql}"
2968
3045
 
2969
3046
  return f", {this_sql}"
2970
3047
 
2971
- if op_sql != "STRAIGHT_JOIN":
3048
+ if join_op:
3049
+ op_sql = join_op
3050
+ elif op_sql != "STRAIGHT_JOIN":
2972
3051
  op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2973
3052
 
2974
3053
  pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2975
- return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"
3054
+ return f"{self.seg(op_sql)} {this_sql}{match_cond}{nearest}{on_sql}{pivots}"
3055
+
3056
+ def nearest_sql(self, expression: exp.Nearest) -> str:
3057
+ if not self.SUPPORTS_NEAREST_JOIN:
3058
+ self.unsupported("NEAREST joins are not supported")
3059
+
3060
+ approximate = "APPROX " if expression.args.get("approximate") else ""
3061
+ metric = "SIMILARITY" if expression.args.get("similarity") else "DISTANCE"
3062
+ return f"{approximate}NEAREST {self.sql(expression, 'count')} BY {metric} {self.sql(expression, 'this')}"
2976
3063
 
2977
3064
  def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
2978
3065
  args = self.expressions(expression, flat=True)
@@ -3636,6 +3723,9 @@ class Generator:
3636
3723
  first = "FIRST" if first else "LAST"
3637
3724
 
3638
3725
  if not partition and not order and not spec and alias:
3726
+ # WINDOW w2 AS w1 is unparseable in DuckDB/Postgres; parens are required.
3727
+ if expression.arg_key == "windows":
3728
+ return f"{this} ({alias})"
3639
3729
  return f"{this} {alias}"
3640
3730
 
3641
3731
  args = self.format_args(
@@ -4085,8 +4175,8 @@ class Generator:
4085
4175
  if this:
4086
4176
  interval_keyword = f"{interval_keyword} " if interval_keyword else ""
4087
4177
  if unit_expression and isinstance(unit_expression, exp.IntervalSpan):
4088
- return f"{interval_keyword}'{this}'{unit}"
4089
- return f"{interval_keyword}'{this}{unit}'"
4178
+ return f"{interval_keyword}{self.sql(exp.Literal.string(this))}{unit}"
4179
+ return f"{interval_keyword}{self.sql(exp.Literal.string(f'{this}{unit}'))}"
4090
4180
  return f"{interval_keyword}{unit}"
4091
4181
 
4092
4182
  this = self.sql(expression, "this")
@@ -4508,13 +4598,17 @@ class Generator:
4508
4598
  return f"{keyword} {items_sql}"
4509
4599
 
4510
4600
  def add_column_sql(self, expression: exp.Expr) -> str:
4511
- sql = self.sql(expression)
4512
- if isinstance(expression, exp.Schema):
4513
- column_text = " COLUMNS"
4514
- elif isinstance(expression, exp.ColumnDef) and self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
4515
- column_text = " COLUMN"
4601
+ column_text = ""
4602
+ if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
4603
+ if isinstance(expression, exp.ColumnDef):
4604
+ column_text = " COLUMN"
4605
+ elif isinstance(expression, exp.Schema):
4606
+ column_text = " COLUMNS"
4607
+
4608
+ if isinstance(expression, exp.Schema) and not self.ALTER_TABLE_ADD_COLUMNS_WRAPPED:
4609
+ sql = self.expressions(expression, flat=True)
4516
4610
  else:
4517
- column_text = ""
4611
+ sql = self.sql(expression)
4518
4612
 
4519
4613
  return f"ADD{column_text} {sql}"
4520
4614
 
@@ -4980,13 +5074,27 @@ class Generator:
4980
5074
 
4981
5075
  then_expression = expression.args.get("then")
4982
5076
  if isinstance(then_expression, exp.Insert):
4983
- this = self.sql(then_expression, "this")
5077
+ this = (
5078
+ "ALL BY NAME"
5079
+ if self.MERGE_ALL_BY_NAME and isinstance(then_expression.this, exp.Star)
5080
+ else self.sql(then_expression, "this")
5081
+ )
4984
5082
  this = f"INSERT {this}" if this else "INSERT"
4985
5083
  then = self.sql(then_expression, "expression")
4986
5084
  then = f"{this} VALUES {then}" if then else this
4987
5085
  elif isinstance(then_expression, exp.Update):
4988
5086
  if isinstance(then_expression.args.get("expressions"), exp.Star):
4989
- then = f"UPDATE {self.sql(then_expression, 'expressions')}"
5087
+ then = (
5088
+ "UPDATE ALL BY NAME"
5089
+ if self.MERGE_ALL_BY_NAME
5090
+ else f"UPDATE {self.sql(then_expression, 'expressions')}"
5091
+ )
5092
+ elif (
5093
+ self.MERGE_ALL_BY_NAME
5094
+ and len(then_expression.expressions) == 1
5095
+ and isinstance(then_expression.expressions[0], exp.Star)
5096
+ ):
5097
+ then = "UPDATE ALL BY NAME"
4990
5098
  else:
4991
5099
  expressions_sql = self.expressions(then_expression)
4992
5100
  then = f"UPDATE SET{self.sep()}{expressions_sql}" if expressions_sql else "UPDATE"
@@ -5947,7 +6055,8 @@ class Generator:
5947
6055
 
5948
6056
  def slice_sql(self, expression: exp.Slice) -> str:
5949
6057
  step = self.sql(expression, "step")
5950
- end = self.sql(expression.expression)
6058
+ # An omitted end is written as `-` before a step, since `::` is the cast operator
6059
+ end = self.sql(expression.expression) or ("-" if step else "")
5951
6060
  begin = self.sql(expression.this)
5952
6061
 
5953
6062
  sql = f"{end}:{step}" if step else end
@@ -429,6 +429,7 @@ class BigQueryGenerator(generator.Generator):
429
429
  exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"),
430
430
  exp.Day: _extract_part("DAY"),
431
431
  exp.DayOfMonth: _extract_part("DAY"),
432
+ exp.DayOfWeek: _extract_part("DAYOFWEEK"),
432
433
  exp.DayOfWeekIso: _day_of_week_iso_sql,
433
434
  exp.DayOfYear: _extract_part("DAYOFYEAR"),
434
435
  exp.Filter: no_filter_sql,
@@ -24,6 +24,7 @@ from sqlglot.dialects.dialect import (
24
24
  )
25
25
  from sqlglot.generator import unsupported_args
26
26
  from sqlglot.helper import is_int
27
+ from sqlglot.optimizer.simplify import simplify_parens
27
28
  from collections import defaultdict
28
29
 
29
30
  DATETIME_DELTA = t.Union[exp.DateAdd, exp.DateDiff, exp.DateSub, exp.TimestampSub, exp.TimestampAdd]
@@ -433,13 +434,12 @@ class ClickHouseGenerator(generator.Generator):
433
434
 
434
435
  @unsupported_args("json_scope")
435
436
  def contains_sql(self, expression: exp.Contains) -> str:
436
- # ClickHouse has no CONTAINS; the comparison needs parentheses inside another operator
437
- result = exp.StrPosition(this=expression.this, substr=expression.expression) > 0
438
- if isinstance(expression.parent, exp.Binary) and not isinstance(
439
- expression.parent, exp.Connector
440
- ):
441
- return self.sql(exp.paren(result, copy=False))
442
- return self.sql(result)
437
+ # ClickHouse has no CONTAINS; keep the parentheses only where the parent needs them
438
+ result = exp.paren(
439
+ exp.StrPosition(this=expression.this, substr=expression.expression) > 0, copy=False
440
+ )
441
+ result.parent = expression.parent
442
+ return self.sql(simplify_parens(result, self.dialect))
443
443
 
444
444
  def groupconcat_sql(self, expression: exp.GroupConcat) -> str:
445
445
  this = expression.this
@@ -19,6 +19,7 @@ class DatabricksGenerator(SparkGenerator):
19
19
  SAFE_JSON_PATH_KEY_RE = exp.SAFE_IDENTIFIER_RE
20
20
  QUOTE_JSON_PATH = False
21
21
  PARSE_JSON_NAME: str | None = "PARSE_JSON"
22
+ SUPPORTS_TEMPORAL_PRECISION = True
22
23
 
23
24
  TRANSFORMS = {
24
25
  k: v