sqlglotc 30.24.0__tar.gz → 30.25.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 (117) hide show
  1. {sqlglotc-30.24.0/sqlglotc.egg-info → sqlglotc-30.25.0}/PKG-INFO +2 -2
  2. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/pyproject.toml +1 -0
  3. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/array.py +4 -0
  4. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/builders.py +1 -1
  5. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/constraints.py +2 -1
  6. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/core.py +7 -8
  7. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/datatypes.py +3 -1
  8. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/ddl.py +8 -0
  9. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/query.py +8 -3
  10. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generator.py +73 -20
  11. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/bigquery.py +1 -0
  12. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/clickhouse.py +7 -7
  13. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/databricks.py +1 -0
  14. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/duckdb.py +72 -7
  15. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/exasol.py +3 -6
  16. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/hive.py +4 -1
  17. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/oracle.py +0 -3
  18. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/postgres.py +35 -2
  19. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/presto.py +7 -2
  20. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/python.py +5 -0
  21. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/snowflake.py +3 -0
  22. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/sqlite.py +6 -2
  23. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/tsql.py +4 -1
  24. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/annotate_types.py +150 -93
  25. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/canonicalize_internal_names.py +11 -10
  26. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/qualify_columns.py +94 -158
  27. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/resolver.py +57 -8
  28. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/scope.py +16 -2
  29. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/simplify.py +5 -1
  30. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parser.py +41 -12
  31. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/clickhouse.py +5 -5
  32. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/duckdb.py +81 -2
  33. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/oracle.py +13 -0
  34. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/postgres.py +33 -5
  35. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/sqlite.py +32 -1
  36. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/tokenizer_core.py +2 -0
  37. {sqlglotc-30.24.0 → sqlglotc-30.25.0/sqlglotc.egg-info}/PKG-INFO +2 -2
  38. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglotc.egg-info/requires.txt +1 -1
  39. sqlglotc-30.25.0/sqlglotc.egg-info/scm_version.json +8 -0
  40. sqlglotc-30.24.0/sqlglotc.egg-info/scm_version.json +0 -8
  41. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/MANIFEST.in +0 -0
  42. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/setup.cfg +0 -0
  43. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/setup.py +0 -0
  44. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/anonymize.py +0 -0
  45. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/errors.py +0 -0
  46. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/executor/table.py +0 -0
  47. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/aggregate.py +0 -0
  48. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/dml.py +0 -0
  49. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/functions.py +0 -0
  50. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/json.py +0 -0
  51. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/math.py +0 -0
  52. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/properties.py +0 -0
  53. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/string.py +0 -0
  54. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/expressions/temporal.py +0 -0
  55. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/athena.py +0 -0
  56. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/dax.py +0 -0
  57. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/doris.py +0 -0
  58. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/dremio.py +0 -0
  59. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/drill.py +0 -0
  60. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/druid.py +0 -0
  61. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/dune.py +0 -0
  62. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/fabric.py +0 -0
  63. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/materialize.py +0 -0
  64. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/mysql.py +0 -0
  65. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/prql.py +0 -0
  66. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/redshift.py +0 -0
  67. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/risingwave.py +0 -0
  68. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/singlestore.py +0 -0
  69. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/solr.py +0 -0
  70. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/spark.py +0 -0
  71. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/spark2.py +0 -0
  72. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/starrocks.py +0 -0
  73. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/tableau.py +0 -0
  74. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/teradata.py +0 -0
  75. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/generators/trino.py +0 -0
  76. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/helper.py +0 -0
  77. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/lineage.py +0 -0
  78. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  79. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  80. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/qualify.py +0 -0
  81. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  82. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/athena.py +0 -0
  83. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/base.py +0 -0
  84. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/bigquery.py +0 -0
  85. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/databricks.py +0 -0
  86. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/dax.py +0 -0
  87. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/doris.py +0 -0
  88. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/dremio.py +0 -0
  89. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/drill.py +0 -0
  90. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/druid.py +0 -0
  91. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/dune.py +0 -0
  92. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/exasol.py +0 -0
  93. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/fabric.py +0 -0
  94. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/hive.py +0 -0
  95. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/materialize.py +0 -0
  96. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/mysql.py +0 -0
  97. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/presto.py +0 -0
  98. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/prql.py +0 -0
  99. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/redshift.py +0 -0
  100. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/risingwave.py +0 -0
  101. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/singlestore.py +0 -0
  102. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/snowflake.py +0 -0
  103. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/solr.py +0 -0
  104. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/spark.py +0 -0
  105. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/spark2.py +0 -0
  106. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/starrocks.py +0 -0
  107. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/tableau.py +0 -0
  108. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/teradata.py +0 -0
  109. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/trino.py +0 -0
  110. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/parsers/tsql.py +0 -0
  111. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/serde.py +0 -0
  112. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/time.py +0 -0
  113. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglot/trie.py +0 -0
  114. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  115. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  116. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
  117. {sqlglotc-30.24.0 → sqlglotc-30.25.0}/sqlglotc.egg-info/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlglotc
3
- Version: 30.24.0
3
+ Version: 30.25.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.25.0
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: setuptools>=61.0; extra == "dev"
13
13
  Requires-Dist: setuptools_scm; extra == "dev"
@@ -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
@@ -3071,7 +3071,7 @@ def alias_(
3071
3071
 
3072
3072
  @t.overload
3073
3073
  def column(
3074
- col: str | Identifier,
3074
+ col: str | Expr,
3075
3075
  table: str | Identifier | None = None,
3076
3076
  db: str | Identifier | None = None,
3077
3077
  catalog: str | Identifier | None = None,
@@ -3085,7 +3085,7 @@ def column(
3085
3085
 
3086
3086
  @t.overload
3087
3087
  def column(
3088
- col: str | Identifier | Star,
3088
+ col: str | Expr,
3089
3089
  table: str | Identifier | None = None,
3090
3090
  db: str | Identifier | None = None,
3091
3091
  catalog: str | Identifier | None = None,
@@ -3111,19 +3111,18 @@ def column(
3111
3111
  Build a Column.
3112
3112
 
3113
3113
  Args:
3114
- col: Column name.
3114
+ col: Column name or expression.
3115
3115
  table: Table name.
3116
3116
  db: Database name.
3117
3117
  catalog: Catalog name.
3118
3118
  fields: Additional fields using dots.
3119
3119
  quoted: Whether to force quotes on the column's identifiers.
3120
- copy: Whether to copy identifiers if passed in.
3120
+ copy: Whether to copy expressions if passed in.
3121
3121
 
3122
3122
  Returns:
3123
3123
  The new Column instance.
3124
3124
  """
3125
- if not isinstance(col, Star):
3126
- col = to_identifier(col, quoted=quoted, copy=copy)
3125
+ col = maybe_copy(col, copy) if isinstance(col, Expr) else to_identifier(col, quoted=quoted)
3127
3126
 
3128
3127
  this: Column | Dot = Column(
3129
3128
  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
 
@@ -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,
@@ -333,6 +333,9 @@ class Generator:
333
333
  # Whether MERGE ... WHEN MATCHED/NOT MATCHED THEN UPDATE/INSERT ... WHERE is supported
334
334
  SUPPORTS_MERGE_WHERE = False
335
335
 
336
+ # Whether MERGE uses ALL BY NAME instead of a star to update or insert all columns
337
+ MERGE_ALL_BY_NAME = False
338
+
336
339
  # Whether the INTERVAL expression works only with values like '1 day'
337
340
  SINGLE_STRING_INTERVAL = False
338
341
 
@@ -370,6 +373,9 @@ class Generator:
370
373
  # Whether directed joins are supported
371
374
  DIRECTED_JOINS = False
372
375
 
376
+ # Whether top-k distance/similarity joins are supported
377
+ SUPPORTS_NEAREST_JOIN = False
378
+
373
379
  # Whether table hints should be generated
374
380
  TABLE_HINTS = True
375
381
 
@@ -414,9 +420,12 @@ class Generator:
414
420
  # SELECT * VALUES into SELECT UNION
415
421
  VALUES_AS_TABLE = True
416
422
 
417
- # Whether the word COLUMN is included when adding a column with ALTER TABLE
423
+ # Whether COLUMN/COLUMNS keywords are included when adding columns with ALTER TABLE.
418
424
  ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
419
425
 
426
+ # Whether ALTER TABLE mutli-column additions are wrapped in parentheses.
427
+ ALTER_TABLE_ADD_COLUMNS_WRAPPED = True
428
+
420
429
  # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
421
430
  UNNEST_WITH_ORDINALITY = True
422
431
 
@@ -1322,7 +1331,8 @@ class Generator:
1322
1331
  return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1323
1332
  options = self.expressions(expression, key="options", flat=True, sep=" ")
1324
1333
  options = f" {options}" if options else ""
1325
- return f"PRIMARY KEY{options}"
1334
+ include = self.sql(expression, "include")
1335
+ return f"PRIMARY KEY{include}{options}"
1326
1336
 
1327
1337
  def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1328
1338
  this = self.sql(expression, "this")
@@ -1334,7 +1344,8 @@ class Generator:
1334
1344
  nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1335
1345
  options = self.expressions(expression, key="options", flat=True, sep=" ")
1336
1346
  options = f" {options}" if options else ""
1337
- return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
1347
+ include = self.sql(expression, "include")
1348
+ return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{include}{options}"
1338
1349
 
1339
1350
  def inoutcolumnconstraint_sql(self, expression: exp.InOutColumnConstraint) -> str:
1340
1351
  input_ = expression.args.get("input_")
@@ -2024,7 +2035,9 @@ class Generator:
2024
2035
  with_storage = self.expressions(expression, key="with_storage", flat=True)
2025
2036
  with_storage = f" WITH ({with_storage})" if with_storage else ""
2026
2037
  tablespace = self.sql(expression, "tablespace")
2027
- tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
2038
+ if tablespace:
2039
+ prefix = "" if isinstance(expression.parent, exp.Index) else " USING INDEX"
2040
+ tablespace = f"{prefix} TABLESPACE {tablespace}"
2028
2041
  on = self.sql(expression, "on")
2029
2042
  on = f" ON {on}" if on else ""
2030
2043
 
@@ -2168,7 +2181,7 @@ class Generator:
2168
2181
  def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
2169
2182
  if isinstance(expression.this, exp.Dot):
2170
2183
  return self.sql(expression, "this")
2171
- return f"'{expression.name}'" if string_key else expression.name
2184
+ return self.sql(exp.Literal.string(expression.name)) if string_key else expression.name
2172
2185
 
2173
2186
  def property_sql(self, expression: exp.Property) -> str:
2174
2187
  property_cls = expression.__class__
@@ -2911,6 +2924,12 @@ class Generator:
2911
2924
  this = self.indent(self.sql(expression, "this"))
2912
2925
  return f"{self.seg('HAVING')}{self.sep()}{this}"
2913
2926
 
2927
+ def connectto_sql(self, expression: exp.ConnectTo) -> str:
2928
+ return f"CONNECT {self.sql(expression, 'this')}"
2929
+
2930
+ def disconnect_sql(self, expression: exp.Disconnect) -> str:
2931
+ return "DISCONNECT"
2932
+
2914
2933
  def connect_sql(self, expression: exp.Connect) -> str:
2915
2934
  start = self.sql(expression, "start")
2916
2935
  start = self.seg(f"START WITH {start}") if start else ""
@@ -2922,7 +2941,7 @@ class Generator:
2922
2941
  def prior_sql(self, expression: exp.Prior) -> str:
2923
2942
  return f"PRIOR {self.sql(expression, 'this')}"
2924
2943
 
2925
- def join_sql(self, expression: exp.Join) -> str:
2944
+ def join_sql(self, expression: exp.Join, join_op: str | None = None) -> str:
2926
2945
  if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2927
2946
  side = None
2928
2947
  else:
@@ -2942,6 +2961,8 @@ class Generator:
2942
2961
  )
2943
2962
  match_cond = self.sql(expression, "match_condition")
2944
2963
  match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2964
+ nearest = self.sql(expression, "nearest")
2965
+ nearest = f" {nearest}" if nearest else ""
2945
2966
  on_sql = self.sql(expression, "on")
2946
2967
  using = expression.args.get("using")
2947
2968
 
@@ -2962,17 +2983,27 @@ class Generator:
2962
2983
  on_sql = f"{space}USING ({on_sql})"
2963
2984
  else:
2964
2985
  on_sql = f"{space}ON {on_sql}"
2965
- elif not op_sql:
2986
+ elif not op_sql and not nearest:
2966
2987
  if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2967
2988
  return f" {this_sql}"
2968
2989
 
2969
2990
  return f", {this_sql}"
2970
2991
 
2971
- if op_sql != "STRAIGHT_JOIN":
2992
+ if join_op:
2993
+ op_sql = join_op
2994
+ elif op_sql != "STRAIGHT_JOIN":
2972
2995
  op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2973
2996
 
2974
2997
  pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2975
- return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"
2998
+ return f"{self.seg(op_sql)} {this_sql}{match_cond}{nearest}{on_sql}{pivots}"
2999
+
3000
+ def nearest_sql(self, expression: exp.Nearest) -> str:
3001
+ if not self.SUPPORTS_NEAREST_JOIN:
3002
+ self.unsupported("NEAREST joins are not supported")
3003
+
3004
+ approximate = "APPROX " if expression.args.get("approximate") else ""
3005
+ metric = "SIMILARITY" if expression.args.get("similarity") else "DISTANCE"
3006
+ return f"{approximate}NEAREST {self.sql(expression, 'count')} BY {metric} {self.sql(expression, 'this')}"
2976
3007
 
2977
3008
  def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
2978
3009
  args = self.expressions(expression, flat=True)
@@ -3636,6 +3667,9 @@ class Generator:
3636
3667
  first = "FIRST" if first else "LAST"
3637
3668
 
3638
3669
  if not partition and not order and not spec and alias:
3670
+ # WINDOW w2 AS w1 is unparseable in DuckDB/Postgres; parens are required.
3671
+ if expression.arg_key == "windows":
3672
+ return f"{this} ({alias})"
3639
3673
  return f"{this} {alias}"
3640
3674
 
3641
3675
  args = self.format_args(
@@ -4085,8 +4119,8 @@ class Generator:
4085
4119
  if this:
4086
4120
  interval_keyword = f"{interval_keyword} " if interval_keyword else ""
4087
4121
  if unit_expression and isinstance(unit_expression, exp.IntervalSpan):
4088
- return f"{interval_keyword}'{this}'{unit}"
4089
- return f"{interval_keyword}'{this}{unit}'"
4122
+ return f"{interval_keyword}{self.sql(exp.Literal.string(this))}{unit}"
4123
+ return f"{interval_keyword}{self.sql(exp.Literal.string(f'{this}{unit}'))}"
4090
4124
  return f"{interval_keyword}{unit}"
4091
4125
 
4092
4126
  this = self.sql(expression, "this")
@@ -4508,13 +4542,17 @@ class Generator:
4508
4542
  return f"{keyword} {items_sql}"
4509
4543
 
4510
4544
  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"
4545
+ column_text = ""
4546
+ if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
4547
+ if isinstance(expression, exp.ColumnDef):
4548
+ column_text = " COLUMN"
4549
+ elif isinstance(expression, exp.Schema):
4550
+ column_text = " COLUMNS"
4551
+
4552
+ if isinstance(expression, exp.Schema) and not self.ALTER_TABLE_ADD_COLUMNS_WRAPPED:
4553
+ sql = self.expressions(expression, flat=True)
4516
4554
  else:
4517
- column_text = ""
4555
+ sql = self.sql(expression)
4518
4556
 
4519
4557
  return f"ADD{column_text} {sql}"
4520
4558
 
@@ -4980,13 +5018,27 @@ class Generator:
4980
5018
 
4981
5019
  then_expression = expression.args.get("then")
4982
5020
  if isinstance(then_expression, exp.Insert):
4983
- this = self.sql(then_expression, "this")
5021
+ this = (
5022
+ "ALL BY NAME"
5023
+ if self.MERGE_ALL_BY_NAME and isinstance(then_expression.this, exp.Star)
5024
+ else self.sql(then_expression, "this")
5025
+ )
4984
5026
  this = f"INSERT {this}" if this else "INSERT"
4985
5027
  then = self.sql(then_expression, "expression")
4986
5028
  then = f"{this} VALUES {then}" if then else this
4987
5029
  elif isinstance(then_expression, exp.Update):
4988
5030
  if isinstance(then_expression.args.get("expressions"), exp.Star):
4989
- then = f"UPDATE {self.sql(then_expression, 'expressions')}"
5031
+ then = (
5032
+ "UPDATE ALL BY NAME"
5033
+ if self.MERGE_ALL_BY_NAME
5034
+ else f"UPDATE {self.sql(then_expression, 'expressions')}"
5035
+ )
5036
+ elif (
5037
+ self.MERGE_ALL_BY_NAME
5038
+ and len(then_expression.expressions) == 1
5039
+ and isinstance(then_expression.expressions[0], exp.Star)
5040
+ ):
5041
+ then = "UPDATE ALL BY NAME"
4990
5042
  else:
4991
5043
  expressions_sql = self.expressions(then_expression)
4992
5044
  then = f"UPDATE SET{self.sep()}{expressions_sql}" if expressions_sql else "UPDATE"
@@ -5947,7 +5999,8 @@ class Generator:
5947
5999
 
5948
6000
  def slice_sql(self, expression: exp.Slice) -> str:
5949
6001
  step = self.sql(expression, "step")
5950
- end = self.sql(expression.expression)
6002
+ # An omitted end is written as `-` before a step, since `::` is the cast operator
6003
+ end = self.sql(expression.expression) or ("-" if step else "")
5951
6004
  begin = self.sql(expression.this)
5952
6005
 
5953
6006
  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
@@ -672,6 +672,15 @@ def _struct_sql(self: DuckDBGenerator, expression: exp.Struct) -> str:
672
672
  return f"ROW({csv_args})" if is_bq_inline_struct else f"{{{csv_args}}}"
673
673
 
674
674
 
675
+ @unsupported_args("unique_keys", "return_type", "encoding", "strict")
676
+ def _json_constructor_sql(self: DuckDBGenerator, expression: exp.JSONArray | exp.JSONObject) -> str:
677
+ if expression.args.get("null_handling") == "ABSENT ON NULL":
678
+ self.unsupported("ABSENT ON NULL is not supported in DuckDB")
679
+
680
+ name = "JSON_ARRAY" if isinstance(expression, exp.JSONArray) else "JSON_OBJECT"
681
+ return self.func(name, *expression.expressions)
682
+
683
+
675
684
  def _json_format_sql(self: DuckDBGenerator, expression: exp.JSONFormat) -> str:
676
685
  sql = self.func("TO_JSON", expression.this, expression.args.get("options"))
677
686
  return f"CAST({sql} AS TEXT)"
@@ -889,6 +898,19 @@ WRAPPED_JSON_EXTRACT_EXPRESSIONS = (exp.Binary, exp.Bracket, exp.In, exp.Not)
889
898
 
890
899
 
891
900
  def _arrow_json_extract_sql(self: DuckDBGenerator, expression: JSON_EXTRACT_TYPE) -> str:
901
+ paths = expression.expressions
902
+ if isinstance(expression, exp.JSONExtract) and paths:
903
+ # JSON_EXTRACT with several paths (SQLite, MySQL) returns a JSON array of the results,
904
+ # which DuckDB builds from a list of paths
905
+ return self.func(
906
+ "TO_JSON",
907
+ self.func(
908
+ "JSON_EXTRACT",
909
+ expression.this,
910
+ exp.Array(expressions=[expression.expression, *paths]),
911
+ ),
912
+ )
913
+
892
914
  arrow_sql = arrow_json_extract_sql(self, expression)
893
915
  if not expression.same_parent and isinstance(
894
916
  expression.parent, WRAPPED_JSON_EXTRACT_EXPRESSIONS
@@ -1567,6 +1589,7 @@ class DuckDBGenerator(generator.Generator):
1567
1589
  TABLE_HINTS = False
1568
1590
  QUERY_HINTS = False
1569
1591
  LIMIT_FETCH = "LIMIT"
1592
+ SUPPORTS_NEAREST_JOIN = True
1570
1593
  STRUCT_DELIMITER = ("(", ")")
1571
1594
  RENAME_TABLE_WITH_DB = False
1572
1595
  NVL2_SUPPORTED = False
@@ -1794,7 +1817,7 @@ class DuckDBGenerator(generator.Generator):
1794
1817
  exp.TsOrDsAdd: _date_delta_to_binary_interval_op(),
1795
1818
  exp.TsOrDsDiff: lambda self, e: self.func(
1796
1819
  "DATE_DIFF",
1797
- f"'{e.args.get('unit') or 'DAY'}'",
1820
+ exp.Literal.string(e.unit or "DAY"),
1798
1821
  exp.cast(e.expression, exp.DType.TIMESTAMP),
1799
1822
  exp.cast(e.this, exp.DType.TIMESTAMP),
1800
1823
  ),
@@ -1824,6 +1847,8 @@ class DuckDBGenerator(generator.Generator):
1824
1847
  ),
1825
1848
  exp.Xor: _xor_sql,
1826
1849
  exp.JSONObjectAgg: rename_func("JSON_GROUP_OBJECT"),
1850
+ exp.JSONArray: _json_constructor_sql,
1851
+ exp.JSONObject: _json_constructor_sql,
1827
1852
  exp.JSONBObjectAgg: rename_func("JSON_GROUP_OBJECT"),
1828
1853
  exp.DateBin: rename_func("TIME_BUCKET"),
1829
1854
  exp.LastDay: _last_day_sql,
@@ -1970,6 +1995,7 @@ class DuckDBGenerator(generator.Generator):
1970
1995
  exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
1971
1996
  exp.ReturnsProperty: exp.Properties.Location.POST_ALIAS,
1972
1997
  exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
1998
+ exp.TriggerProperties: exp.Properties.Location.POST_EXPRESSION,
1973
1999
  exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
1974
2000
  }
1975
2001
 
@@ -2387,6 +2413,27 @@ class DuckDBGenerator(generator.Generator):
2387
2413
 
2388
2414
  return self.sql(add_expr)
2389
2415
 
2416
+ def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
2417
+ # DuckDB has no Unicode escape strings and doesn't read \u in a string, so the escapes
2418
+ # are decoded into the characters, e.g. U&'d\0061t\0061' and U&'d!+000061ta' UESCAPE '!'
2419
+ # are both 'data'
2420
+ escape = expression.args.get("escape")
2421
+ escape_char = re.escape(escape.name if isinstance(escape, exp.Expr) else "\\")
2422
+ pattern = re.compile(
2423
+ rf"{escape_char}(?:\+([0-9a-fA-F]{{6}})|([0-9a-fA-F]{{4}})|({escape_char}))"
2424
+ )
2425
+
2426
+ def decode(match: re.Match[str]) -> str:
2427
+ if match.group(3):
2428
+ return match.group(3)
2429
+ return chr(int(match.group(1) or match.group(2), 16))
2430
+
2431
+ # A character outside the BMP can be written as a UTF-16 surrogate pair
2432
+ text = (
2433
+ pattern.sub(decode, expression.name).encode("utf-16", "surrogatepass").decode("utf-16")
2434
+ )
2435
+ return self.sql(exp.Literal.string(text))
2436
+
2390
2437
  def bytestring_sql(self, expression: exp.ByteString) -> str:
2391
2438
  if not expression.args.get("is_bytes"):
2392
2439
  return super().bytestring_sql(expression)
@@ -2551,9 +2598,6 @@ class DuckDBGenerator(generator.Generator):
2551
2598
  initial_value = expression.args.get("initial")
2552
2599
  merge_lambda = expression.args.get("merge")
2553
2600
 
2554
- if merge_lambda:
2555
- merge_lambda.set("colon", True)
2556
-
2557
2601
  return self.func("list_reduce", array_arg, merge_lambda, initial_value)
2558
2602
 
2559
2603
  def zipf_sql(self, expression: exp.Zipf) -> str:
@@ -2678,7 +2722,7 @@ class DuckDBGenerator(generator.Generator):
2678
2722
  return self._greatest_least_sql(expression)
2679
2723
 
2680
2724
  def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
2681
- if expression.args.get("colon"):
2725
+ if self.dialect.version >= (1, 3):
2682
2726
  prefix = "LAMBDA "
2683
2727
  arrow_sep = ":"
2684
2728
  wrap = False
@@ -2697,6 +2741,11 @@ class DuckDBGenerator(generator.Generator):
2697
2741
  self.unsupported("SOUNDEX is not supported in DuckDB")
2698
2742
  return self.func("SOUNDEX", expression.this)
2699
2743
 
2744
+ def placeholder_sql(self, expression: exp.Placeholder) -> str:
2745
+ if isinstance(expression.this, exp.Identifier):
2746
+ return f"${self.sql(expression, 'this')}"
2747
+ return super().placeholder_sql(expression)
2748
+
2700
2749
  def install_sql(self, expression: exp.Install) -> str:
2701
2750
  force = "FORCE " if expression.args.get("force") else ""
2702
2751
  this = self.sql(expression, "this")
@@ -3163,10 +3212,26 @@ class DuckDBGenerator(generator.Generator):
3163
3212
  )
3164
3213
  return super().in_sql(expression)
3165
3214
 
3166
- def join_sql(self, expression: exp.Join) -> str:
3215
+ def interval_sql(self, expression: exp.Interval) -> str:
3216
+ # DuckDB truncates INTERVAL '1.5' DAY to 1 day, but keeps the fraction in INTERVAL '1.5 DAY'
3217
+ this = expression.this
3218
+ unit = expression.unit
3219
+ if expression.args.get("fraction_in_string") and this and isinstance(unit, exp.Var):
3220
+ return f"INTERVAL '{this.name} {self.sql(unit)}'"
3221
+
3222
+ return super().interval_sql(expression)
3223
+
3224
+ def join_sql(self, expression: exp.Join, join_op: str | None = None) -> str:
3225
+ if expression.kind in ("MARK", "SINGLE") or (
3226
+ expression.side == "RIGHT" and expression.is_semi_or_anti_join
3227
+ ):
3228
+ kind = "_".join(part for part in (expression.side, expression.kind) if part)
3229
+ return super().join_sql(expression, join_op=f"JOIN BY (TYPE {kind})")
3230
+
3167
3231
  if (
3168
3232
  not expression.args.get("using")
3169
3233
  and not expression.args.get("on")
3234
+ and not expression.args.get("nearest")
3170
3235
  and not expression.method
3171
3236
  and (expression.kind in ("", "INNER", "OUTER"))
3172
3237
  ):
@@ -3178,7 +3243,7 @@ class DuckDBGenerator(generator.Generator):
3178
3243
  expression.set("side", None)
3179
3244
  expression.set("kind", None)
3180
3245
 
3181
- return super().join_sql(expression)
3246
+ return super().join_sql(expression, join_op=join_op)
3182
3247
 
3183
3248
  def countif_sql(self, expression: exp.CountIf) -> str:
3184
3249
  if self.dialect.version >= (1, 2):
@@ -100,12 +100,9 @@ def _trunc_sql(
100
100
  node = expression.this.this if isinstance(expression.this, exp.Cast) else expression.this
101
101
  expr_sql = self.sql(node)
102
102
  if isinstance(node, exp.Literal) and node.is_string:
103
- expr_sql = (
104
- f"{kind} '{node.this.replace('T', ' ')}'"
105
- if kind == "TIMESTAMP"
106
- else f"DATE '{node.this}'"
107
- )
108
- return f"DATE_TRUNC('{unit}', {expr_sql})"
103
+ text = node.this.replace("T", " ") if kind == "TIMESTAMP" else node.this
104
+ expr_sql = f"{kind} {self.sql(exp.Literal.string(text))}"
105
+ return f"DATE_TRUNC({self.sql(exp.Literal.string(unit))}, {expr_sql})"
109
106
 
110
107
 
111
108
  def _date_trunc_sql(self: ExasolGenerator, expression: exp.DateTrunc) -> str:
@@ -246,6 +246,7 @@ class HiveGenerator(generator.Generator):
246
246
  PARSE_JSON_NAME: str | None = "PARSE_JSON"
247
247
  PAD_FILL_PATTERN_IS_REQUIRED = True
248
248
  SUPPORTS_MEDIAN = False
249
+ SUPPORTS_TEMPORAL_PRECISION = False
249
250
  ARRAY_SIZE_NAME = "SIZE"
250
251
  ALTER_SET_TYPE = ""
251
252
 
@@ -506,7 +507,9 @@ class HiveGenerator(generator.Generator):
506
507
  expression.set("expressions", None)
507
508
  elif expression.is_type(exp.DType.TEXT) and expression.expressions:
508
509
  expression.set("this", exp.DType.VARCHAR)
509
- elif expression.this in exp.DataType.TEMPORAL_TYPES:
510
+ elif (
511
+ not self.SUPPORTS_TEMPORAL_PRECISION and expression.this in exp.DataType.TEMPORAL_TYPES
512
+ ):
510
513
  expression.set("expressions", None)
511
514
  elif expression.is_type("float"):
512
515
  size_expression = expression.find(exp.DataTypeParam)
@@ -145,9 +145,6 @@ class OracleGenerator(generator.Generator):
145
145
  def offset_sql(self, expression: exp.Offset) -> str:
146
146
  return f"{super().offset_sql(expression)} ROWS"
147
147
 
148
- def add_column_sql(self, expression: exp.Expr) -> str:
149
- return f"ADD {self.sql(expression)}"
150
-
151
148
  def queryoption_sql(self, expression: exp.QueryOption) -> str:
152
149
  option = self.sql(expression, "this")
153
150
  value = self.sql(expression, "expression")