sqlglotc 30.17.0__tar.gz → 30.19.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 (119) hide show
  1. {sqlglotc-30.17.0/sqlglotc.egg-info → sqlglotc-30.19.0}/PKG-INFO +3 -3
  2. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/pyproject.toml +2 -2
  3. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/executor/table.py +1 -1
  4. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/aggregate.py +6 -0
  5. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/array.py +6 -1
  6. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/builders.py +9 -4
  7. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/constraints.py +4 -0
  8. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/core.py +4 -0
  9. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/ddl.py +1 -1
  10. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/json.py +8 -0
  11. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/query.py +21 -5
  12. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/string.py +3 -1
  13. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generator.py +72 -21
  14. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/clickhouse.py +12 -6
  15. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/databricks.py +1 -1
  16. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/doris.py +15 -0
  17. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/dremio.py +3 -0
  18. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/duckdb.py +19 -21
  19. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/fabric.py +5 -5
  20. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/hive.py +1 -0
  21. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/mysql.py +12 -2
  22. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/oracle.py +2 -1
  23. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/postgres.py +8 -4
  24. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/presto.py +1 -1
  25. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/python.py +20 -3
  26. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/singlestore.py +11 -0
  27. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/snowflake.py +4 -35
  28. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/spark.py +3 -0
  29. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/sqlite.py +54 -25
  30. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/teradata.py +3 -3
  31. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/trino.py +42 -1
  32. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/tsql.py +72 -4
  33. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/lineage.py +1 -1
  34. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/annotate_types.py +34 -19
  35. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/canonicalize_internal_names.py +4 -4
  36. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/normalize_identifiers.py +33 -11
  37. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/qualify_columns.py +193 -25
  38. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/qualify_tables.py +10 -0
  39. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/resolver.py +25 -10
  40. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/scope.py +34 -24
  41. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/simplify.py +73 -43
  42. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parser.py +160 -82
  43. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/bigquery.py +2 -7
  44. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/clickhouse.py +27 -6
  45. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/dremio.py +6 -0
  46. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/hive.py +6 -0
  47. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/mysql.py +60 -7
  48. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/oracle.py +1 -0
  49. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/postgres.py +7 -2
  50. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/redshift.py +2 -0
  51. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/snowflake.py +18 -37
  52. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/spark2.py +16 -2
  53. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/sqlite.py +5 -0
  54. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/starrocks.py +3 -0
  55. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/teradata.py +1 -1
  56. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/trino.py +0 -6
  57. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/tsql.py +5 -3
  58. {sqlglotc-30.17.0 → sqlglotc-30.19.0/sqlglotc.egg-info}/PKG-INFO +3 -3
  59. sqlglotc-30.19.0/sqlglotc.egg-info/requires.txt +6 -0
  60. sqlglotc-30.19.0/sqlglotc.egg-info/scm_version.json +8 -0
  61. sqlglotc-30.17.0/sqlglotc.egg-info/requires.txt +0 -6
  62. sqlglotc-30.17.0/sqlglotc.egg-info/scm_version.json +0 -8
  63. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/MANIFEST.in +0 -0
  64. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/setup.cfg +0 -0
  65. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/setup.py +0 -0
  66. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/anonymize.py +0 -0
  67. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/errors.py +0 -0
  68. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/datatypes.py +0 -0
  69. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/dml.py +0 -0
  70. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/functions.py +0 -0
  71. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/math.py +0 -0
  72. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/properties.py +0 -0
  73. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/expressions/temporal.py +0 -0
  74. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/athena.py +0 -0
  75. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/bigquery.py +0 -0
  76. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/dax.py +0 -0
  77. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/drill.py +0 -0
  78. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/druid.py +0 -0
  79. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/dune.py +0 -0
  80. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/exasol.py +0 -0
  81. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/materialize.py +0 -0
  82. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/prql.py +0 -0
  83. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/redshift.py +0 -0
  84. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/risingwave.py +0 -0
  85. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/solr.py +0 -0
  86. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/spark2.py +0 -0
  87. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/starrocks.py +0 -0
  88. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/generators/tableau.py +0 -0
  89. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/helper.py +0 -0
  90. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  91. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/optimizer/qualify.py +0 -0
  92. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/athena.py +0 -0
  93. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/base.py +0 -0
  94. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/databricks.py +0 -0
  95. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/dax.py +0 -0
  96. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/doris.py +0 -0
  97. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/drill.py +0 -0
  98. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/druid.py +0 -0
  99. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/duckdb.py +0 -0
  100. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/dune.py +0 -0
  101. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/exasol.py +0 -0
  102. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/fabric.py +0 -0
  103. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/materialize.py +0 -0
  104. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/presto.py +0 -0
  105. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/prql.py +0 -0
  106. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/risingwave.py +0 -0
  107. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/singlestore.py +0 -0
  108. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/solr.py +0 -0
  109. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/spark.py +0 -0
  110. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/parsers/tableau.py +0 -0
  111. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/schema.py +0 -0
  112. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/serde.py +0 -0
  113. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/time.py +0 -0
  114. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/tokenizer_core.py +0 -0
  115. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglot/trie.py +0 -0
  116. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  117. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  118. {sqlglotc-30.17.0 → sqlglotc-30.19.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
  119. {sqlglotc-30.17.0 → sqlglotc-30.19.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.17.0
3
+ Version: 30.19.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.17.0
10
+ Requires-Dist: sqlglot==30.19.0
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: setuptools>=61.0; extra == "dev"
13
13
  Requires-Dist: setuptools_scm; extra == "dev"
14
- Requires-Dist: sqlglot-mypy>=2.3.0.post1; extra == "dev"
14
+ Requires-Dist: sqlglot-mypy>=2.3.0.post2; 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", "sqlglot-mypy >= 2.3.0.post1"]
10
+ dev = ["setuptools >= 61.0", "setuptools_scm", "sqlglot-mypy >= 2.3.0.post2"]
11
11
 
12
12
  [project.urls]
13
13
  Homepage = "https://sqlglot.com/"
@@ -17,7 +17,7 @@ Repository = "https://github.com/tobymao/sqlglot"
17
17
  requires = [
18
18
  "setuptools >= 61.0",
19
19
  "setuptools_scm",
20
- "sqlglot-mypy >= 2.3.0.post1",
20
+ "sqlglot-mypy >= 2.3.0.post2",
21
21
  "types-python-dateutil",
22
22
  "sqlglot",
23
23
  ]
@@ -109,7 +109,7 @@ class RowReader:
109
109
  if columns is not None
110
110
  else {}
111
111
  )
112
- self.row = None
112
+ self.row = ()
113
113
 
114
114
  def __getitem__(self, column):
115
115
  return self.row[self.columns[column]]
@@ -5,6 +5,12 @@ from __future__ import annotations
5
5
  from sqlglot.expressions.core import Expression, Func, AggFunc, Binary
6
6
 
7
7
 
8
+ class Agg(Expression, AggFunc):
9
+ @property
10
+ def output_name(self) -> str:
11
+ return self.this.name
12
+
13
+
8
14
  class AIAgg(Expression, AggFunc):
9
15
  arg_types = {"this": True, "expression": True}
10
16
  _sql_names = ["AI_AGG"]
@@ -240,10 +240,15 @@ class Explode(Expression, Func, UDTF):
240
240
  is_var_len_args = True
241
241
 
242
242
 
243
- class Inline(Expression, Func):
243
+ class Inline(Expression, Func, UDTF):
244
244
  pass
245
245
 
246
246
 
247
+ class Stack(Expression, Func, UDTF):
248
+ arg_types = {"this": True, "expressions": True}
249
+ is_var_len_args = True
250
+
251
+
247
252
  @trait
248
253
  class ExplodeOuter(Expr):
249
254
  pass
@@ -495,11 +495,16 @@ def cast(
495
495
  target_dialect = Dialect.get_or_raise(dialect)
496
496
  type_mapping = target_dialect.generator_class.TYPE_MAPPING
497
497
 
498
- existing_cast_type: DType = expr.to.this
498
+ existing_cast_type = expr.to.this
499
499
  new_cast_type: DType = data_type.this
500
- types_are_equivalent = type_mapping.get(
501
- existing_cast_type, existing_cast_type.value
502
- ) == type_mapping.get(new_cast_type, new_cast_type.value)
500
+ # `this` is only a plain type enum for simple types; complex ones such as
501
+ # INTERVAL nest another expression there, so the equivalence check is skipped.
502
+ types_are_equivalent = (
503
+ isinstance(existing_cast_type, DType)
504
+ and isinstance(new_cast_type, DType)
505
+ and type_mapping.get(existing_cast_type, existing_cast_type.value)
506
+ == type_mapping.get(new_cast_type, new_cast_type.value)
507
+ )
503
508
 
504
509
  if expr.is_type(data_type) or types_are_equivalent:
505
510
  return expr
@@ -41,6 +41,10 @@ class ZeroFillColumnConstraint(ColumnConstraint):
41
41
  arg_types = {}
42
42
 
43
43
 
44
+ class BinaryColumnConstraint(ColumnConstraint):
45
+ arg_types = {}
46
+
47
+
44
48
  class PeriodForSystemTimeConstraint(Expression, ColumnConstraintKind):
45
49
  arg_types = {"this": True, "expression": True}
46
50
 
@@ -1828,6 +1828,10 @@ class Identifier(Expression):
1828
1828
  class DynamicIdentifier(Expression, Func):
1829
1829
  arg_types = {"this": True, "expressions": False}
1830
1830
 
1831
+ @property
1832
+ def name(self) -> str:
1833
+ return self.this.name if self.this else ""
1834
+
1831
1835
 
1832
1836
  class Opclass(Expression):
1833
1837
  arg_types = {"this": True, "expression": True}
@@ -345,8 +345,8 @@ class MergeTreeTTL(Expression):
345
345
 
346
346
  class Drop(Expression):
347
347
  arg_types = {
348
- "this": False,
349
348
  "kind": False,
349
+ "tables": False,
350
350
  "expressions": False,
351
351
  "exists": False,
352
352
  "temporary": False,
@@ -54,6 +54,10 @@ class JSONBContainsAllTopKeys(Expression, Binary, Predicate, Func):
54
54
  pass
55
55
 
56
56
 
57
+ class JSONBContainsTopKey(Expression, Binary, Predicate, Func):
58
+ pass
59
+
60
+
57
61
  class JSONBContainsAnyTopKeys(Expression, Binary, Predicate, Func):
58
62
  pass
59
63
 
@@ -236,6 +240,10 @@ class OpenJSON(Expression, Func):
236
240
  arg_types = {"this": True, "path": False, "expressions": False}
237
241
 
238
242
 
243
+ class FromJson(Expression, Func):
244
+ arg_types = {"this": True, "expression": True, "options": False}
245
+
246
+
239
247
  class ParseJSON(Expression, Func):
240
248
  # BigQuery, Snowflake have PARSE_JSON, Presto has JSON_PARSE
241
249
  # Snowflake also has TRY_PARSE_JSON, which is represented using `safe`
@@ -12,6 +12,7 @@ from sqlglot.expressions.core import (
12
12
  Condition,
13
13
  Distinct,
14
14
  Dot,
15
+ DynamicIdentifier,
15
16
  Expr,
16
17
  Expression,
17
18
  Func,
@@ -966,9 +967,10 @@ class Table(Expression, Selectable):
966
967
 
967
968
  @property
968
969
  def name(self) -> str:
969
- if not self.this or isinstance(self.this, Func):
970
+ this = self.this
971
+ if not this or (isinstance(this, Func) and not isinstance(this, DynamicIdentifier)):
970
972
  return ""
971
- return self.this.name
973
+ return this.name
972
974
 
973
975
  @property
974
976
  def db(self) -> str:
@@ -1018,6 +1020,20 @@ class Table(Expression, Selectable):
1018
1020
  return col
1019
1021
 
1020
1022
 
1023
+ def _is_star(expression: Expr) -> bool:
1024
+ stack = [expression]
1025
+ while stack:
1026
+ node = stack.pop()
1027
+ if isinstance(node, SetOperation):
1028
+ stack.append(node.this)
1029
+ stack.append(node.expression)
1030
+ elif isinstance(node, Subquery):
1031
+ stack.append(node.this)
1032
+ elif node.is_star:
1033
+ return True
1034
+ return False
1035
+
1036
+
1021
1037
  class SetOperation(Expression, Query):
1022
1038
  arg_types = {
1023
1039
  "with_": False,
@@ -1060,7 +1076,7 @@ class SetOperation(Expression, Query):
1060
1076
 
1061
1077
  @property
1062
1078
  def is_star(self) -> bool:
1063
- return self.this.is_star or self.expression.is_star
1079
+ return _is_star(self)
1064
1080
 
1065
1081
  @property
1066
1082
  def selects(self) -> list[Expr]:
@@ -1718,7 +1734,7 @@ class Subquery(Expression, DerivedTable, Query):
1718
1734
 
1719
1735
  @property
1720
1736
  def is_star(self) -> bool:
1721
- return self.this.is_star
1737
+ return _is_star(self)
1722
1738
 
1723
1739
  @property
1724
1740
  def output_name(self) -> str:
@@ -1891,7 +1907,7 @@ class Where(Expression):
1891
1907
  class Analyze(Expression):
1892
1908
  arg_types = {
1893
1909
  "kind": False,
1894
- "this": False,
1910
+ "tables": False,
1895
1911
  "options": False,
1896
1912
  "mode": False,
1897
1913
  "partition": False,
@@ -32,6 +32,7 @@ class Concat(Expression, Func):
32
32
 
33
33
 
34
34
  class ConcatWs(Concat):
35
+ arg_types = {**Concat.arg_types, "flatten": False}
35
36
  _sql_names = ["CONCAT_WS"]
36
37
 
37
38
 
@@ -478,7 +479,8 @@ class RegexpReplace(Expression, Func):
478
479
 
479
480
 
480
481
  class RegexpSplit(Expression, Func):
481
- arg_types = {"this": True, "expression": True, "limit": False}
482
+ # "mode" is Dremio-specific, appended after "limit" for from_arg_list compat
483
+ arg_types = {"this": True, "expression": True, "limit": False, "mode": False}
482
484
 
483
485
 
484
486
  class RegexpSubstr(Expression, Func):
@@ -147,6 +147,7 @@ class Generator:
147
147
  exp.AssumeColumnConstraint: lambda self, e: f"ASSUME ({self.sql(e, 'this')})",
148
148
  exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
149
149
  exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
150
+ exp.BinaryColumnConstraint: lambda *_: "BINARY",
150
151
  exp.CaseSpecificColumnConstraint: lambda _, e: (
151
152
  f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC"
152
153
  ),
@@ -206,6 +207,7 @@ class Generator:
206
207
  exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DType.BIGINT)),
207
208
  exp.JSONBContainsAnyTopKeys: lambda self, e: self.binary(e, "?|"),
208
209
  exp.JSONBContainsAllTopKeys: lambda self, e: self.binary(e, "?&"),
210
+ exp.JSONBContainsTopKey: lambda self, e: self.binary(e, "?"),
209
211
  exp.JSONBDeleteAtPath: lambda self, e: self.binary(e, "#-"),
210
212
  exp.JSONBPathExists: lambda self, e: self.binary(e, "@?"),
211
213
  exp.JSONObject: lambda self, e: self._jsonobject_sql(e),
@@ -351,6 +353,9 @@ class Generator:
351
353
  # The separator for grouping sets and rollups
352
354
  GROUPINGS_SEP = ","
353
355
 
356
+ # Whether GROUPING SETS can follow GROUP BY expressions without a comma
357
+ SUPPORTS_GROUPING_SETS_AS_SUFFIX = False
358
+
354
359
  # The string used for creating an index on a table
355
360
  INDEX_ON = "ON"
356
361
 
@@ -530,6 +535,12 @@ class Generator:
530
535
  # True means limit 1 happens after the set op, False means it it happens on y.
531
536
  SET_OP_MODIFIERS = True
532
537
 
538
+ # Whether a SELECT operand can have a branch-local LIMIT/TOP without parentheses.
539
+ SET_OP_LIMITS = False
540
+
541
+ # Whether set operation operands can be parenthesized without a SELECT wrapper.
542
+ SET_OP_PARENTHESIZED_OPERANDS = True
543
+
533
544
  # Whether parameters from COPY statement are wrapped in parentheses
534
545
  COPY_PARAMS_ARE_WRAPPED = True
535
546
 
@@ -850,6 +861,16 @@ class Generator:
850
861
 
851
862
  RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: t.ClassVar[tuple[type[exp.Expr], ...]] = ()
852
863
 
864
+ MOD_OPERATOR = "%"
865
+
866
+ # Infix operators that bind at least as tightly as %, so a Mod on their right side needs parentheses
867
+ MOD_PAREN_PARENT_TYPES: t.ClassVar[tuple[type[exp.Expr], ...]] = (
868
+ exp.Mul,
869
+ exp.Div,
870
+ exp.IntDiv,
871
+ exp.Mod,
872
+ )
873
+
853
874
  SAFE_JSON_PATH_KEY_RE: t.ClassVar = exp.SAFE_IDENTIFIER_RE
854
875
 
855
876
  SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
@@ -1826,7 +1847,7 @@ class Generator:
1826
1847
  return self.prepend_ctes(expression, f"DELETE{hint}{tables}{expression_sql}")
1827
1848
 
1828
1849
  def drop_sql(self, expression: exp.Drop) -> str:
1829
- this = self.sql(expression, "this")
1850
+ tables = self.expressions(expression, key="tables", flat=True)
1830
1851
  expressions = self.expressions(expression, flat=True)
1831
1852
  expressions = f" ({expressions})" if expressions else ""
1832
1853
  kind = expression.args["kind"]
@@ -1848,7 +1869,7 @@ class Generator:
1848
1869
  purge = " PURGE" if expression.args.get("purge") else ""
1849
1870
  sync = " SYNC" if expression.args.get("sync") else ""
1850
1871
  force = " FORCE" if expression.args.get("force") else ""
1851
- return f"DROP{temporary}{materialized}{iceberg} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{restrict}{constraints}{purge}{sync}{force}"
1872
+ return f"DROP{temporary}{materialized}{iceberg} {kind}{concurrently_sql}{exists_sql}{tables}{on_cluster}{expressions}{cascade}{restrict}{constraints}{purge}{sync}{force}"
1852
1873
 
1853
1874
  def set_operation(self, expression: exp.SetOperation) -> str:
1854
1875
  op_type = type(expression)
@@ -1887,16 +1908,16 @@ class Generator:
1887
1908
  if not self.SET_OP_MODIFIERS:
1888
1909
  limit = expression.args.get("limit")
1889
1910
  order = expression.args.get("order")
1911
+ offset = expression.args.get("offset")
1890
1912
 
1891
- if limit or order:
1913
+ if limit or order or offset:
1892
1914
  select = self._move_ctes_to_top_level(
1893
1915
  exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1894
1916
  )
1895
1917
 
1896
- if limit:
1897
- select = select.limit(limit.pop(), copy=False)
1898
- if order:
1899
- select = select.order_by(order.pop(), copy=False)
1918
+ for arg in ("limit", "order", "offset"):
1919
+ if value := expression.args.get(arg):
1920
+ select.set(arg, value.pop())
1900
1921
  return self.sql(select)
1901
1922
 
1902
1923
  sqls: list[str] = []
@@ -1914,6 +1935,14 @@ class Generator:
1914
1935
  )
1915
1936
  stack.append(node.this)
1916
1937
  else:
1938
+ if (
1939
+ not self.SET_OP_LIMITS
1940
+ and isinstance(node, exp.Select)
1941
+ and node.args.get("limit")
1942
+ ):
1943
+ node = node.subquery(copy=False)
1944
+ if not self.SET_OP_PARENTHESIZED_OPERANDS:
1945
+ node = exp.select("*").from_(node, copy=False)
1917
1946
  sqls.append(self.sql(node))
1918
1947
 
1919
1948
  this = self.sep().join(sqls)
@@ -2819,7 +2848,18 @@ class Generator:
2819
2848
  and groupings
2820
2849
  and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2821
2850
  ):
2822
- group_by = f"{group_by}{self.GROUPINGS_SEP}"
2851
+ add_separator = True
2852
+
2853
+ if grouping_sets:
2854
+ if self.SUPPORTS_GROUPING_SETS_AS_SUFFIX:
2855
+ add_separator = False
2856
+ else:
2857
+ self.unsupported(
2858
+ "GROUPING SETS without a comma after GROUP BY expressions is not supported"
2859
+ )
2860
+
2861
+ if add_separator:
2862
+ group_by = f"{group_by}{self.GROUPINGS_SEP}"
2823
2863
 
2824
2864
  return f"{group_by}{groupings}"
2825
2865
 
@@ -3298,8 +3338,8 @@ class Generator:
3298
3338
  self.sql(expression, "order"),
3299
3339
  *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
3300
3340
  *self.after_limit_modifiers(expression),
3301
- self.options_modifier(expression),
3302
3341
  self.sql(expression, "for_"),
3342
+ self.options_modifier(expression),
3303
3343
  sep="",
3304
3344
  )
3305
3345
 
@@ -3796,6 +3836,7 @@ class Generator:
3796
3836
  path = self.expressions(expression, sep="", flat=True).lstrip(".")
3797
3837
 
3798
3838
  if self.QUOTE_JSON_PATH:
3839
+ path = self.escape_str(path)
3799
3840
  path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
3800
3841
 
3801
3842
  return path
@@ -3814,7 +3855,7 @@ class Generator:
3814
3855
 
3815
3856
  if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
3816
3857
  escaped = expression.replace("'", "\\'")
3817
- escaped = f"\\'{expression}\\'"
3858
+ escaped = f"'{escaped}'"
3818
3859
  else:
3819
3860
  escaped = expression.replace('"', '\\"')
3820
3861
  escaped = f'"{escaped}"'
@@ -4587,7 +4628,15 @@ class Generator:
4587
4628
  return self.binary(expression, "<=")
4588
4629
 
4589
4630
  def mod_sql(self, expression: exp.Mod) -> str:
4590
- return self.binary(expression, "%")
4631
+ this = self.sql(expression, "this")
4632
+ expr = self.sql(expression, "expression")
4633
+ sql = f"{this} {self.maybe_comment(self.MOD_OPERATOR, comments=expression.comments)} {expr}"
4634
+
4635
+ parent = expression.parent
4636
+ if isinstance(parent, self.MOD_PAREN_PARENT_TYPES) and parent.expression is expression:
4637
+ return f"({sql})"
4638
+
4639
+ return sql
4591
4640
 
4592
4641
  def mul_sql(self, expression: exp.Mul) -> str:
4593
4642
  return self.binary(expression, "*")
@@ -5035,6 +5084,12 @@ class Generator:
5035
5084
 
5036
5085
  return self.sql(case)
5037
5086
 
5087
+ def nthvalue_sql(self, expression: exp.NthValue) -> str:
5088
+ if expression.args.get("from_first") is False:
5089
+ self.unsupported("NTH_VALUE FROM LAST is not supported")
5090
+
5091
+ return self.function_fallback_sql(expression)
5092
+
5038
5093
  def comprehension_sql(self, expression: exp.Comprehension) -> str:
5039
5094
  this = self.sql(expression, "this")
5040
5095
  expr = self.sql(expression, "expression")
@@ -5361,12 +5416,6 @@ class Generator:
5361
5416
 
5362
5417
  this = self.json_path_part(this)
5363
5418
 
5364
- if quoted and self.QUOTE_JSON_PATH:
5365
- # The whole path is rendered as a single quoted string literal, so the bracketed key
5366
- # (which may itself contain backslash-escaped quotes, e.g. ["x \"y\"z"]) must be
5367
- # escaped again for the outer string literal (-> ["x \\"y\\"z"]).
5368
- this = self.escape_str(this)
5369
-
5370
5419
  return (
5371
5420
  f"[{this}]"
5372
5421
  if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
@@ -5395,9 +5444,11 @@ class Generator:
5395
5444
 
5396
5445
  if self.IGNORE_NULLS_IN_FUNC and not expression.meta_get("inline"):
5397
5446
  if self.IGNORE_NULLS_BEFORE_ORDER:
5447
+ from sqlglot.optimizer.scope import find_all_in_scope
5448
+
5398
5449
  # The first modifier here will be the one closest to the AggFunc's arg
5399
5450
  mods = sorted(
5400
- expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
5451
+ find_all_in_scope(expression, exp.HavingMax, exp.Order, exp.Limit),
5401
5452
  key=lambda x: (
5402
5453
  0
5403
5454
  if isinstance(x, exp.HavingMax)
@@ -6037,8 +6088,8 @@ class Generator:
6037
6088
  options = f" {options}" if options else ""
6038
6089
  kind = self.sql(expression, "kind")
6039
6090
  kind = f" {kind}" if kind else ""
6040
- this = self.sql(expression, "this")
6041
- this = f" {this}" if this else ""
6091
+ tables = self.expressions(expression, key="tables", flat=True)
6092
+ tables = f" {tables}" if tables else ""
6042
6093
  mode = self.sql(expression, "mode")
6043
6094
  mode = f" {mode}" if mode else ""
6044
6095
  properties = self.sql(expression, "properties")
@@ -6047,7 +6098,7 @@ class Generator:
6047
6098
  partition = f" {partition}" if partition else ""
6048
6099
  inner_expression = self.sql(expression, "expression")
6049
6100
  inner_expression = f" {inner_expression}" if inner_expression else ""
6050
- return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
6101
+ return f"ANALYZE{options}{kind}{tables}{partition}{mode}{inner_expression}{properties}"
6051
6102
 
6052
6103
  def xmltable_sql(self, expression: exp.XMLTable) -> str:
6053
6104
  this = self.sql(expression, "this")
@@ -186,6 +186,7 @@ class ClickHouseGenerator(generator.Generator):
186
186
  TABLE_HINTS = False
187
187
  GROUPINGS_SEP = ""
188
188
  SET_OP_MODIFIERS = False
189
+ SET_OP_LIMITS = True
189
190
  ARRAY_SIZE_NAME = "LENGTH"
190
191
  WRAP_DERIVED_VALUES = False
191
192
  AUTO_REFRESH_BARE_INTERVALS = True
@@ -372,12 +373,8 @@ class ClickHouseGenerator(generator.Generator):
372
373
  exp.SchemaCommentProperty: lambda self, e: self.naked_property(e),
373
374
  exp.Stddev: rename_func("stddevSamp"),
374
375
  exp.Chr: rename_func("CHAR"),
375
- exp.Lag: lambda self, e: self.func(
376
- "lagInFrame", e.this, e.args.get("offset"), e.args.get("default")
377
- ),
378
- exp.Lead: lambda self, e: self.func(
379
- "leadInFrame", e.this, e.args.get("offset"), e.args.get("default")
380
- ),
376
+ exp.Lag: rename_func("lag"),
377
+ exp.Lead: rename_func("lead"),
381
378
  exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
382
379
  rename_func("editDistance")
383
380
  ),
@@ -449,6 +446,15 @@ class ClickHouseGenerator(generator.Generator):
449
446
 
450
447
  return self.func("groupConcat", this)
451
448
 
449
+ def select_sql(self, expression: exp.Select) -> str:
450
+ limit = expression.args.get("limit")
451
+ if isinstance(limit, exp.Fetch) and not expression.args.get("order"):
452
+ count = limit.args.get("count")
453
+ expression.set(
454
+ "limit", exp.Limit(expression=count if count is not None else exp.Literal.number(1))
455
+ )
456
+ return super().select_sql(expression)
457
+
452
458
  def offset_sql(self, expression: exp.Offset) -> str:
453
459
  offset = super().offset_sql(expression)
454
460
 
@@ -97,10 +97,10 @@ class DatabricksGenerator(SparkGenerator):
97
97
  return f"{self.sql(expression, 'this')} TIMESERIES"
98
98
 
99
99
  def jsonpath_sql(self, expression: exp.JSONPath) -> str:
100
- expression.set("escape", None)
101
100
  path = super().jsonpath_sql(expression)
102
101
 
103
102
  if isinstance(expression.parent, exp.JSONExtractScalar):
103
+ path = self.escape_str(path)
104
104
  return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
105
105
 
106
106
  return path
@@ -104,6 +104,7 @@ class DorisGenerator(MySQLGenerator):
104
104
  "alter",
105
105
  "analyze",
106
106
  "analyzed",
107
+ "analyzer",
107
108
  "and",
108
109
  "anti",
109
110
  "append",
@@ -111,6 +112,7 @@ class DorisGenerator(MySQLGenerator):
111
112
  "array_range",
112
113
  "as",
113
114
  "asc",
115
+ "asof",
114
116
  "at",
115
117
  "authors",
116
118
  "auto",
@@ -132,6 +134,7 @@ class DorisGenerator(MySQLGenerator):
132
134
  "bitxor",
133
135
  "blob",
134
136
  "boolean",
137
+ "both",
135
138
  "brief",
136
139
  "broker",
137
140
  "buckets",
@@ -148,6 +151,7 @@ class DorisGenerator(MySQLGenerator):
148
151
  "catalogs",
149
152
  "chain",
150
153
  "char",
154
+ "char_filter",
151
155
  "character",
152
156
  "charset",
153
157
  "check",
@@ -227,6 +231,7 @@ class DorisGenerator(MySQLGenerator):
227
231
  "drop",
228
232
  "dropp",
229
233
  "dual",
234
+ "dump",
230
235
  "duplicate",
231
236
  "dynamic",
232
237
  "else",
@@ -329,8 +334,10 @@ class DorisGenerator(MySQLGenerator):
329
334
  "largeint",
330
335
  "last",
331
336
  "lateral",
337
+ "layout",
332
338
  "ldap",
333
339
  "ldap_admin_password",
340
+ "leading",
334
341
  "left",
335
342
  "less",
336
343
  "level",
@@ -352,6 +359,7 @@ class DorisGenerator(MySQLGenerator):
352
359
  "match",
353
360
  "match_all",
354
361
  "match_any",
362
+ "match_condition",
355
363
  "match_phrase",
356
364
  "match_phrase_edge",
357
365
  "match_phrase_prefix",
@@ -377,6 +385,7 @@ class DorisGenerator(MySQLGenerator):
377
385
  "next",
378
386
  "ngram_bf",
379
387
  "no",
388
+ "no_use_mv",
380
389
  "non_nullable",
381
390
  "not",
382
391
  "null",
@@ -410,6 +419,7 @@ class DorisGenerator(MySQLGenerator):
410
419
  "permissive",
411
420
  "physical",
412
421
  "plan",
422
+ "play",
413
423
  "process",
414
424
  "plugin",
415
425
  "plugins",
@@ -520,6 +530,9 @@ class DorisGenerator(MySQLGenerator):
520
530
  "timestampdiff",
521
531
  "tinyint",
522
532
  "to",
533
+ "token_filter",
534
+ "tokenizer",
535
+ "trailing",
523
536
  "transaction",
524
537
  "trash",
525
538
  "tree",
@@ -527,6 +540,7 @@ class DorisGenerator(MySQLGenerator):
527
540
  "trim",
528
541
  "true",
529
542
  "truncate",
543
+ "try_cast",
530
544
  "type",
531
545
  "type_cast",
532
546
  "types",
@@ -539,6 +553,7 @@ class DorisGenerator(MySQLGenerator):
539
553
  "unsigned",
540
554
  "update",
541
555
  "use",
556
+ "use_mv",
542
557
  "user",
543
558
  "using",
544
559
  "value",
@@ -70,6 +70,9 @@ class DremioGenerator(generator.Generator):
70
70
  exp.DateAdd: _date_delta_sql("DATE_ADD"),
71
71
  exp.DateSub: _date_delta_sql("DATE_SUB"),
72
72
  exp.GenerateSeries: rename_func("ARRAY_GENERATE_RANGE"),
73
+ exp.RegexpSplit: lambda self, e: self.func(
74
+ "REGEXP_SPLIT", e.this, e.expression, e.args.get("mode"), e.args.get("limit")
75
+ ),
73
76
  }
74
77
 
75
78
  def version_sql(self, expression: exp.Version) -> str: