sqlglotc 30.22.0__tar.gz → 30.23.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.22.0/sqlglotc.egg-info → sqlglotc-30.23.0}/PKG-INFO +2 -2
  2. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/anonymize.py +1 -0
  3. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/builders.py +54 -5
  4. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/constraints.py +1 -0
  5. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/core.py +13 -3
  6. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/ddl.py +2 -0
  7. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/json.py +1 -0
  8. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/properties.py +17 -0
  9. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/query.py +9 -0
  10. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generator.py +13 -1
  11. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/athena.py +5 -1
  12. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/clickhouse.py +33 -1
  13. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/doris.py +6 -0
  14. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/duckdb.py +9 -2
  15. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/oracle.py +22 -0
  16. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/presto.py +2 -1
  17. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/spark2.py +2 -1
  18. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/starrocks.py +21 -0
  19. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/teradata.py +19 -0
  20. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/qualify_columns.py +3 -3
  21. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/qualify_tables.py +4 -1
  22. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parser.py +32 -12
  23. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/athena.py +8 -0
  24. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/clickhouse.py +5 -1
  25. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/doris.py +40 -0
  26. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/duckdb.py +7 -0
  27. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/mysql.py +13 -0
  28. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/oracle.py +77 -2
  29. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/postgres.py +1 -0
  30. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/spark2.py +7 -4
  31. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/starrocks.py +72 -1
  32. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/teradata.py +44 -0
  33. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/tokenizer_core.py +3 -1
  34. {sqlglotc-30.22.0 → sqlglotc-30.23.0/sqlglotc.egg-info}/PKG-INFO +2 -2
  35. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglotc.egg-info/requires.txt +1 -1
  36. sqlglotc-30.23.0/sqlglotc.egg-info/scm_version.json +8 -0
  37. sqlglotc-30.22.0/sqlglotc.egg-info/scm_version.json +0 -8
  38. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/MANIFEST.in +0 -0
  39. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/pyproject.toml +0 -0
  40. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/setup.cfg +0 -0
  41. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/setup.py +0 -0
  42. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/errors.py +0 -0
  43. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/executor/table.py +0 -0
  44. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/aggregate.py +0 -0
  45. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/array.py +0 -0
  46. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/datatypes.py +0 -0
  47. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/dml.py +0 -0
  48. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/functions.py +0 -0
  49. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/math.py +0 -0
  50. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/string.py +0 -0
  51. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/expressions/temporal.py +0 -0
  52. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/bigquery.py +0 -0
  53. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/databricks.py +0 -0
  54. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/dax.py +0 -0
  55. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/dremio.py +0 -0
  56. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/drill.py +0 -0
  57. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/druid.py +0 -0
  58. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/dune.py +0 -0
  59. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/exasol.py +0 -0
  60. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/fabric.py +0 -0
  61. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/hive.py +0 -0
  62. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/materialize.py +0 -0
  63. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/mysql.py +0 -0
  64. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/postgres.py +0 -0
  65. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/prql.py +0 -0
  66. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/python.py +0 -0
  67. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/redshift.py +0 -0
  68. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/risingwave.py +0 -0
  69. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/singlestore.py +0 -0
  70. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/snowflake.py +0 -0
  71. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/solr.py +0 -0
  72. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/spark.py +0 -0
  73. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/sqlite.py +0 -0
  74. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/tableau.py +0 -0
  75. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/trino.py +0 -0
  76. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/generators/tsql.py +0 -0
  77. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/helper.py +0 -0
  78. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/lineage.py +0 -0
  79. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/annotate_types.py +0 -0
  80. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
  81. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  82. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  83. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/qualify.py +0 -0
  84. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/resolver.py +0 -0
  85. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/scope.py +0 -0
  86. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/optimizer/simplify.py +0 -0
  87. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/base.py +0 -0
  88. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/bigquery.py +0 -0
  89. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/databricks.py +0 -0
  90. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/dax.py +0 -0
  91. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/dremio.py +0 -0
  92. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/drill.py +0 -0
  93. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/druid.py +0 -0
  94. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/dune.py +0 -0
  95. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/exasol.py +0 -0
  96. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/fabric.py +0 -0
  97. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/hive.py +0 -0
  98. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/materialize.py +0 -0
  99. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/presto.py +0 -0
  100. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/prql.py +0 -0
  101. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/redshift.py +0 -0
  102. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/risingwave.py +0 -0
  103. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/singlestore.py +0 -0
  104. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/snowflake.py +0 -0
  105. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/solr.py +0 -0
  106. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/spark.py +0 -0
  107. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/sqlite.py +0 -0
  108. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/tableau.py +0 -0
  109. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/trino.py +0 -0
  110. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/parsers/tsql.py +0 -0
  111. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/serde.py +0 -0
  112. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/time.py +0 -0
  113. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglot/trie.py +0 -0
  114. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
  115. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  116. {sqlglotc-30.22.0 → sqlglotc-30.23.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
  117. {sqlglotc-30.22.0 → sqlglotc-30.23.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.22.0
3
+ Version: 30.23.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.22.0
10
+ Requires-Dist: sqlglot==30.23.0
11
11
  Provides-Extra: dev
12
12
  Requires-Dist: setuptools>=61.0; extra == "dev"
13
13
  Requires-Dist: setuptools_scm; extra == "dev"
@@ -15,6 +15,7 @@ ANONYMIZED_TYPES = {
15
15
  TokenType.HEX_STRING,
16
16
  TokenType.HEREDOC_STRING,
17
17
  TokenType.IDENTIFIER,
18
+ TokenType.NATIONAL_RAW_STRING,
18
19
  TokenType.NATIONAL_STRING,
19
20
  TokenType.NUMBER,
20
21
  TokenType.RAW_STRING,
@@ -10,14 +10,25 @@ from sqlglot.errors import ParseError, TokenError
10
10
  from sqlglot.expressions.core import (
11
11
  Alias,
12
12
  Anonymous,
13
+ AtTimeZone,
14
+ Between,
15
+ Binary,
16
+ BitwiseNot,
13
17
  Boolean,
18
+ Bracket,
14
19
  Column,
15
20
  Condition,
21
+ Dot,
16
22
  EQ,
17
23
  Expr,
24
+ Func,
18
25
  Identifier,
26
+ In,
19
27
  Literal,
28
+ Neg,
29
+ Not,
20
30
  Null,
31
+ Paren,
21
32
  Placeholder,
22
33
  TABLE_PARTS,
23
34
  Var,
@@ -36,6 +47,8 @@ from sqlglot.expressions.query import (
36
47
  From,
37
48
  Schema,
38
49
  Select,
50
+ SetOperation,
51
+ Subquery,
39
52
  Table,
40
53
  TableAlias,
41
54
  Tuple,
@@ -44,7 +57,7 @@ from sqlglot.expressions.query import (
44
57
  With,
45
58
  Query,
46
59
  )
47
- from sqlglot.expressions.ddl import Alter, AlterRename, RenameColumn
60
+ from sqlglot.expressions.ddl import Alter, AlterRename, Create, RenameColumn
48
61
  from sqlglot.expressions.dml import Delete, Insert, Merge, Update, When, Whens
49
62
  from sqlglot.expressions.functions import Case, Cast
50
63
  from sqlglot.expressions.array import Array
@@ -53,11 +66,10 @@ from sqlglot.expressions.array import Array
53
66
  if t.TYPE_CHECKING:
54
67
  from collections.abc import Sequence, Iterable, Iterator
55
68
  from sqlglot.dialects.dialect import DialectType
56
- from sqlglot.expressions.core import ExpOrStr, Func
69
+ from sqlglot.expressions.core import ExpOrStr
57
70
  from sqlglot.expressions.datatypes import DATA_TYPE
58
71
  from sqlglot._typing import ParserArgs, ParserNoDialectArgs, E, P
59
72
  from typing_extensions import Unpack, Concatenate
60
- from sqlglot.expressions.core import Dot
61
73
 
62
74
 
63
75
  def select(
@@ -849,6 +861,43 @@ def replace_tables(
849
861
  return expression.transform(_replace_tables, copy=copy) # type: ignore
850
862
 
851
863
 
864
+ OPERATORS = (AtTimeZone, Between, Binary, BitwiseNot, In, Neg, Not)
865
+
866
+
867
+ def _is_operand(placeholder: Placeholder) -> bool:
868
+ parent = placeholder.parent
869
+ if isinstance(parent, (Bracket, In)):
870
+ return placeholder.arg_key == "this"
871
+ return isinstance(parent, OPERATORS)
872
+
873
+
874
+ def _takes_query(placeholder: Placeholder) -> bool:
875
+ parent = placeholder.parent
876
+ if isinstance(parent, In):
877
+ return placeholder.arg_key == "expressions" and len(parent.expressions) == 1
878
+ if isinstance(parent, Func):
879
+ return len(list(parent.iter_expressions())) == 1
880
+ if isinstance(parent, Table):
881
+ return isinstance(parent.parent, (Create, Subquery))
882
+ return not parent or isinstance(parent, (CTE, Paren, SetOperation))
883
+
884
+
885
+ def _replace_placeholder(placeholder: Placeholder, expression: Expr) -> Expr:
886
+ if (
887
+ isinstance(expression, Query)
888
+ and not isinstance(expression, Subquery)
889
+ and not _takes_query(placeholder)
890
+ ):
891
+ return expression.subquery(copy=False)
892
+ if (
893
+ _is_operand(placeholder)
894
+ and isinstance(expression, OPERATORS)
895
+ and not isinstance(expression, Dot)
896
+ ):
897
+ return Paren(this=expression)
898
+ return expression
899
+
900
+
852
901
  def replace_placeholders(expression: Expr, *args: object, **kwargs: t.Any) -> Expr:
853
902
  """Replace placeholders in an expression.
854
903
 
@@ -874,10 +923,10 @@ def replace_placeholders(expression: Expr, *args: object, **kwargs: t.Any) -> Ex
874
923
  if node.this:
875
924
  new_name = kwargs.get(node.this)
876
925
  if new_name is not None:
877
- return convert(new_name)
926
+ return _replace_placeholder(node, convert(new_name))
878
927
  else:
879
928
  try:
880
- return convert(next(args))
929
+ return _replace_placeholder(node, convert(next(args)))
881
930
  except StopIteration:
882
931
  pass
883
932
  return node
@@ -208,6 +208,7 @@ class Constraint(Expression):
208
208
 
209
209
  class ForeignKey(Expression):
210
210
  arg_types = {
211
+ "this": False,
211
212
  "expressions": False,
212
213
  "reference": False,
213
214
  "delete": False,
@@ -867,7 +867,7 @@ class Expression(Expr):
867
867
  for k in sorted(node.args):
868
868
  v = node.args[k]
869
869
  if v:
870
- hash_ = hash((hash_, k, v))
870
+ hash_ = hash((hash_, k, tuple(v) if type(v) is list else v))
871
871
  else:
872
872
  for k in sorted(node.args):
873
873
  v = node.args[k]
@@ -1595,7 +1595,14 @@ class Uncache(Expression):
1595
1595
 
1596
1596
 
1597
1597
  class Refresh(Expression):
1598
- arg_types = {"this": True, "kind": True}
1598
+ arg_types = {
1599
+ "this": True,
1600
+ "kind": True,
1601
+ "partition_start": False,
1602
+ "partition_end": False,
1603
+ "force": False,
1604
+ "mode": False,
1605
+ }
1599
1606
 
1600
1607
 
1601
1608
  class LockingStatement(Expression):
@@ -1859,6 +1866,7 @@ class SessionParameter(Expression, Condition):
1859
1866
 
1860
1867
  class Placeholder(Expression, Condition):
1861
1868
  arg_types = {"this": False, "kind": False, "widget": False, "jdbc": False}
1869
+ _hash_raw_args = True
1862
1870
 
1863
1871
  @property
1864
1872
  def name(self) -> str:
@@ -1994,6 +2002,7 @@ class Typeof(Expression, Func):
1994
2002
 
1995
2003
  class ParameterizedAgg(Expression, AggFunc):
1996
2004
  arg_types = {"this": True, "expressions": True, "params": True}
2005
+ _hash_raw_args = True
1997
2006
 
1998
2007
 
1999
2008
  class Anonymous(Expression, Func):
@@ -2008,6 +2017,7 @@ class Anonymous(Expression, Func):
2008
2017
  class AnonymousAggFunc(Expression, AggFunc):
2009
2018
  arg_types = {"this": True, "expressions": False}
2010
2019
  is_var_len_args = True
2020
+ _hash_raw_args = True
2011
2021
 
2012
2022
 
2013
2023
  class CombinedAggFunc(AnonymousAggFunc):
@@ -2211,7 +2221,7 @@ class Like(Expression, Binary, Predicate):
2211
2221
 
2212
2222
 
2213
2223
  class Match(Expression, Binary, Predicate):
2214
- pass
2224
+ arg_types = {"this": True, "expression": True, "kind": False, "analyzer": False}
2215
2225
 
2216
2226
 
2217
2227
  class LT(Expression, Binary, Predicate):
@@ -176,6 +176,7 @@ class Set(Expression):
176
176
 
177
177
  class Heredoc(Expression):
178
178
  arg_types = {"this": True, "tag": False}
179
+ _hash_raw_args = True
179
180
 
180
181
 
181
182
  class SetItem(Expression):
@@ -382,6 +383,7 @@ class Undrop(Expression):
382
383
 
383
384
  class Command(Expression):
384
385
  arg_types = {"this": True, "expression": False}
386
+ _hash_raw_args = True
385
387
 
386
388
 
387
389
  class Transaction(Expression):
@@ -100,6 +100,7 @@ class JSONExists(Expression, Func):
100
100
  "on_condition": False,
101
101
  "from_dcolonqmark": False,
102
102
  }
103
+ _hash_raw_args = True
103
104
 
104
105
 
105
106
  class JSONExtract(Expression, Binary, Func):
@@ -25,6 +25,10 @@ class AllowedValuesProperty(Expression):
25
25
  arg_types = {"expressions": True}
26
26
 
27
27
 
28
+ class AggregateKeyProperty(Property):
29
+ arg_types = {"expressions": True}
30
+
31
+
28
32
  class AlgorithmProperty(Property):
29
33
  arg_types = {"this": True}
30
34
 
@@ -123,6 +127,7 @@ class DatabaseProperty(Property):
123
127
 
124
128
  class DefinerProperty(Property):
125
129
  arg_types = {"this": True}
130
+ _hash_raw_args = True
126
131
 
127
132
 
128
133
  class DistKeyProperty(Property):
@@ -390,6 +395,10 @@ class PartitionedOfProperty(Property):
390
395
  arg_types = {"this": True, "expression": True}
391
396
 
392
397
 
398
+ class PhysicalProperty(Property):
399
+ arg_types = {"this": True, "value": False}
400
+
401
+
393
402
  class StreamingTableProperty(Property):
394
403
  arg_types = {}
395
404
 
@@ -497,6 +506,10 @@ class StorageHandlerProperty(Property):
497
506
  arg_types = {"this": True}
498
507
 
499
508
 
509
+ class StorageProperty(Property):
510
+ arg_types = {"expressions": True}
511
+
512
+
500
513
  class UsingProperty(Property):
501
514
  # kind: JAR, FILE, or ARCHIVE; this: the resource path (string literal)
502
515
  arg_types = {"this": True, "kind": True}
@@ -522,6 +535,10 @@ class Tags(Property, ColumnConstraintKind):
522
535
  arg_types = {"expressions": True}
523
536
 
524
537
 
538
+ class TableSpaceProperty(Property):
539
+ arg_types = {"this": True}
540
+
541
+
525
542
  class PropertiesLocation(AutoName):
526
543
  POST_CREATE = auto()
527
544
  POST_NAME = auto()
@@ -500,14 +500,17 @@ class HexString(Expression, Condition):
500
500
  class ByteString(Expression, Condition):
501
501
  arg_types = {"this": True, "is_bytes": False}
502
502
  is_primitive = True
503
+ _hash_raw_args = True
503
504
 
504
505
 
505
506
  class RawString(Expression, Condition):
506
507
  is_primitive = True
508
+ _hash_raw_args = True
507
509
 
508
510
 
509
511
  class UnicodeString(Expression, Condition):
510
512
  arg_types = {"this": True, "escape": False}
513
+ _hash_raw_args = True
511
514
 
512
515
 
513
516
  class ColumnPosition(Expression):
@@ -599,6 +602,7 @@ class Introducer(Expression):
599
602
 
600
603
  class National(Expression):
601
604
  is_primitive = True
605
+ _hash_raw_args = True
602
606
 
603
607
 
604
608
  class Partition(Expression):
@@ -2047,14 +2051,17 @@ class JSONPathPart(Expression):
2047
2051
 
2048
2052
  class JSONPathFilter(JSONPathPart):
2049
2053
  arg_types = {"this": True}
2054
+ _hash_raw_args = True
2050
2055
 
2051
2056
 
2052
2057
  class JSONPathKey(JSONPathPart):
2053
2058
  arg_types = {"this": True, "quoted": False}
2059
+ _hash_raw_args = True
2054
2060
 
2055
2061
 
2056
2062
  class JSONPathRecursive(JSONPathPart):
2057
2063
  arg_types = {"this": False}
2064
+ _hash_raw_args = True
2058
2065
 
2059
2066
 
2060
2067
  class JSONPathRoot(JSONPathPart):
@@ -2063,6 +2070,7 @@ class JSONPathRoot(JSONPathPart):
2063
2070
 
2064
2071
  class JSONPathScript(JSONPathPart):
2065
2072
  arg_types = {"this": True}
2073
+ _hash_raw_args = True
2066
2074
 
2067
2075
 
2068
2076
  class JSONPathSlice(JSONPathPart):
@@ -2079,6 +2087,7 @@ class JSONPathSubscript(JSONPathPart):
2079
2087
 
2080
2088
  class JSONPathUnion(JSONPathPart):
2081
2089
  arg_types = {"expressions": True}
2090
+ _hash_raw_args = True
2082
2091
 
2083
2092
 
2084
2093
  class JSONPathWildcard(JSONPathPart):
@@ -706,6 +706,7 @@ class Generator:
706
706
  EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.ClassVar[set[str]] = set()
707
707
 
708
708
  PROPERTIES_LOCATION: t.ClassVar = {
709
+ exp.AggregateKeyProperty: exp.Properties.Location.POST_SCHEMA,
709
710
  exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
710
711
  exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
711
712
  exp.ApiProperty: exp.Properties.Location.POST_CREATE,
@@ -773,6 +774,7 @@ class Generator:
773
774
  exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
774
775
  exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
775
776
  exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
777
+ exp.PhysicalProperty: exp.Properties.Location.UNSUPPORTED,
776
778
  exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
777
779
  exp.Property: exp.Properties.Location.POST_WITH,
778
780
  exp.RefreshTriggerProperty: exp.Properties.Location.POST_SCHEMA,
@@ -800,9 +802,11 @@ class Generator:
800
802
  exp.SqlSecurityProperty: exp.Properties.Location.POST_SCHEMA,
801
803
  exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
802
804
  exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
805
+ exp.StorageProperty: exp.Properties.Location.UNSUPPORTED,
803
806
  exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
804
807
  exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
805
808
  exp.Tags: exp.Properties.Location.POST_WITH,
809
+ exp.TableSpaceProperty: exp.Properties.Location.UNSUPPORTED,
806
810
  exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
807
811
  exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
808
812
  exp.TransientProperty: exp.Properties.Location.POST_CREATE,
@@ -3840,6 +3844,8 @@ class Generator:
3840
3844
  return f"CHECK ({this})"
3841
3845
 
3842
3846
  def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
3847
+ this = self.sql(expression, "this")
3848
+ this = f" {this}" if this else ""
3843
3849
  expressions = self.expressions(expression, flat=True)
3844
3850
  expressions = f" ({expressions})" if expressions else ""
3845
3851
  reference = self.sql(expression, "reference")
@@ -3850,7 +3856,7 @@ class Generator:
3850
3856
  update = f" ON UPDATE {update}" if update else ""
3851
3857
  options = self.expressions(expression, key="options", flat=True, sep=" ")
3852
3858
  options = f" {options}" if options else ""
3853
- return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
3859
+ return f"FOREIGN KEY{this}{expressions}{reference}{delete}{update}{options}"
3854
3860
 
3855
3861
  def primarykey_sql(self, expression: exp.PrimaryKey) -> str:
3856
3862
  this = self.sql(expression, "this")
@@ -4688,6 +4694,8 @@ class Generator:
4688
4694
  return self._like_sql(expression)
4689
4695
 
4690
4696
  def match_sql(self, expression: exp.Match) -> str:
4697
+ if expression.args.get("kind") or expression.args.get("analyzer"):
4698
+ self.unsupported("MATCH variants and USING ANALYZER are not supported in this dialect")
4691
4699
  return self.binary(expression, "MATCH")
4692
4700
 
4693
4701
  def similarto_sql(self, expression: exp.SimilarTo) -> str:
@@ -5043,6 +5051,9 @@ class Generator:
5043
5051
  def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
5044
5052
  return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
5045
5053
 
5054
+ def aggregatekeyproperty_sql(self, expression: exp.AggregateKeyProperty) -> str:
5055
+ return f"AGGREGATE KEY ({self.expressions(expression, flat=True)})"
5056
+
5046
5057
  def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
5047
5058
  return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
5048
5059
 
@@ -5281,6 +5292,7 @@ class Generator:
5281
5292
  expression_sql = self.sql(expression, "expression")
5282
5293
  return f"FOR {this} DO {expression_sql}"
5283
5294
 
5295
+ @unsupported_args("partition_start", "partition_end", "force", "mode")
5284
5296
  def refresh_sql(self, expression: exp.Refresh) -> str:
5285
5297
  this = self.sql(expression, "this")
5286
5298
  kind = "" if isinstance(expression.this, exp.Literal) else f"{expression.text('kind')} "
@@ -54,7 +54,11 @@ def _generate_as_hive(expression: exp.Expr) -> bool:
54
54
 
55
55
  # Any CREATE TABLE other than CREATE TABLE ... As <query> is Hive
56
56
  if not isinstance(expression.expression, exp.Query):
57
- return True
57
+ # CTAS with a leading USING EXTERNAL FUNCTION clause is Trino
58
+ return not (
59
+ isinstance(expression.expression, exp.Command)
60
+ and expression.expression.text("this").upper() == "USING"
61
+ )
58
62
  else:
59
63
  # CREATE VIEW is Trino, but CREATE SCHEMA, CREATE DATABASE, etc, is Hive
60
64
  return expression.kind != "VIEW"
@@ -326,7 +326,7 @@ class ClickHouseGenerator(generator.Generator):
326
326
  exp.IsNan: rename_func("isNaN"),
327
327
  exp.JarowinklerSimilarity: jarowinkler_similarity("jaroWinklerSimilarity"),
328
328
  exp.JSONCast: _json_cast_sql,
329
- exp.JSONExtract: json_extract_segments("JSONExtractString", quoted_index=False),
329
+ exp.JSONExtract: json_extract_segments("JSONExtractRaw", quoted_index=False),
330
330
  exp.JSONExtractScalar: json_extract_segments("JSONExtractString", quoted_index=False),
331
331
  exp.JSONPathKey: json_path_key_only_name,
332
332
  exp.JSONPathRoot: lambda *_: "",
@@ -503,6 +503,14 @@ class ClickHouseGenerator(generator.Generator):
503
503
  if isinstance(this, exp.StrToDate) and expression.to == exp.DType.DATETIME.into_expr():
504
504
  return self.sql(this)
505
505
 
506
+ this = this.unnest()
507
+ if (
508
+ isinstance(this, exp.JSONExtract)
509
+ and this.args.get("requires_json")
510
+ and expression.to.is_type(*exp.DataType.TEXT_TYPES)
511
+ ):
512
+ return self.func("JSONExtract", this, exp.Literal.string("Nullable(String)"))
513
+
506
514
  return super().cast_sql(expression, safe_prefix=safe_prefix)
507
515
 
508
516
  def trycast_sql(self, expression: exp.TryCast) -> str:
@@ -543,6 +551,30 @@ class ClickHouseGenerator(generator.Generator):
543
551
  def neq_sql(self, expression: exp.NEQ) -> str:
544
552
  return self._any_to_has(expression, super().neq_sql, "NOT ")
545
553
 
554
+ def regexpcount_sql(self, expression: exp.RegexpCount) -> str:
555
+ this = expression.this
556
+ pattern = expression.expression
557
+ position = expression.args.get("position")
558
+ parameters = expression.args.get("parameters")
559
+
560
+ if position:
561
+ # SUBSTRING counts bytes in ClickHouse, the position counts characters
562
+ this = self.func("substringUTF8", this, position)
563
+
564
+ if parameters:
565
+ flags = parameters.name if parameters.is_string else ""
566
+ if not parameters.is_string or set(flags) - set("cims"):
567
+ self.unsupported("Only literal 'c', 'i', 'm' and 's' regexp flags are supported")
568
+ # "c" (case-sensitive) is the default, and the last of "c" and "i" wins
569
+ if flags.rfind("c") > flags.rfind("i"):
570
+ flags = flags.replace("i", "")
571
+ flags = "".join(f for f in flags if f in "ims")
572
+ if flags:
573
+ # countMatches has no flags argument, so they are embedded in the pattern
574
+ pattern = self.func("CONCAT", exp.Literal.string(f"(?{flags})"), pattern)
575
+
576
+ return self.func("countMatches", this, pattern)
577
+
546
578
  def regexpilike_sql(self, expression: exp.RegexpILike) -> str:
547
579
  # Manually add a flag to make the search case-insensitive
548
580
  regex = self.func("CONCAT", "'(?i)'", expression.expression)
@@ -23,6 +23,12 @@ def _lag_lead_sql(self, expression: exp.Lag | exp.Lead) -> str:
23
23
 
24
24
 
25
25
  class DorisGenerator(MySQLGenerator):
26
+ def match_sql(self, expression: exp.Match) -> str:
27
+ kind = expression.text("kind")
28
+ sql = self.binary(expression, f"MATCH_{kind}" if kind else "MATCH")
29
+ analyzer = self.sql(expression, "analyzer")
30
+ return f"{sql} USING ANALYZER {analyzer}" if analyzer else sql
31
+
26
32
  LAST_DAY_SUPPORTS_DATE_PART = False
27
33
  SUPPORTS_ALTER_COLUMN_NULLABILITY = False
28
34
  VARCHAR_REQUIRES_SIZE = False
@@ -388,6 +388,10 @@ def _array_insert_sql(self: DuckDBGenerator, expression: exp.ArrayInsert) -> str
388
388
 
389
389
  pos_value = position.to_py()
390
390
 
391
+ # 1-based -1 appends (Spark/Databricks); the 0-based normalization below
392
+ # would otherwise map it onto the prepend branch.
393
+ append = index_offset == 1 and pos_value == -1
394
+
391
395
  # Normalize one-based indexing to zero-based for slice calculations
392
396
  # Spark (1-based) -> Snowflake (0-based):
393
397
  # Positive: pos=1 -> pos=0 (subtract 1)
@@ -399,7 +403,10 @@ def _array_insert_sql(self: DuckDBGenerator, expression: exp.ArrayInsert) -> str
399
403
  pos_value = pos_value + index_offset
400
404
 
401
405
  # Build the appropriate list_concat expression based on position
402
- if pos_value == 0:
406
+ if append:
407
+ # append at end
408
+ concat_exprs = [this, element_array]
409
+ elif pos_value == 0:
403
410
  # insert at beginning
404
411
  concat_exprs = [element_array, this]
405
412
  elif pos_value > 0:
@@ -4164,7 +4171,7 @@ class DuckDBGenerator(generator.Generator):
4164
4171
  this=exp.Transform(
4165
4172
  this=expression.this,
4166
4173
  expression=exp.Lambda(
4167
- this=exp.Coalesce(this=x, expressions=[null]),
4174
+ this=exp.Coalesce(this=exp.cast(x, "TEXT"), expressions=[null]),
4168
4175
  expressions=[x],
4169
4176
  ),
4170
4177
  ),
@@ -106,13 +106,24 @@ class OracleGenerator(generator.Generator):
106
106
  exp.UtcTimestamp: rename_func("UTC_TIMESTAMP"),
107
107
  exp.UtcTime: rename_func("UTC_TIME"),
108
108
  exp.Systimestamp: lambda self, e: "SYSTIMESTAMP",
109
+ exp.TableSpaceProperty: lambda self, e: f"TABLESPACE {self.sql(e, 'this')}",
109
110
  }
110
111
 
111
112
  PROPERTIES_LOCATION = {
112
113
  **generator.Generator.PROPERTIES_LOCATION,
113
114
  exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED,
115
+ exp.TableSpaceProperty: exp.Properties.Location.POST_SCHEMA,
116
+ exp.PhysicalProperty: exp.Properties.Location.POST_SCHEMA,
117
+ exp.StorageProperty: exp.Properties.Location.POST_SCHEMA,
114
118
  }
115
119
 
120
+ def physicalproperty_sql(self, expression: exp.PhysicalProperty) -> str:
121
+ value = self.sql(expression, "value")
122
+ return f"{expression.name}{f' {value}' if value else ''}"
123
+
124
+ def storageproperty_sql(self, expression: exp.StorageProperty) -> str:
125
+ return f"STORAGE ({self.expressions(expression, sep=' ', flat=True)})"
126
+
116
127
  def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
117
128
  if expression.args.get("sysdate"):
118
129
  return "SYSDATE"
@@ -120,6 +131,17 @@ class OracleGenerator(generator.Generator):
120
131
  this = expression.this
121
132
  return self.func("CURRENT_TIMESTAMP", this) if this else "CURRENT_TIMESTAMP"
122
133
 
134
+ def version_sql(self, expression: exp.Version) -> str:
135
+ if expression.text("kind") == "AS OF":
136
+ value = expression.expression
137
+ value_sql = (
138
+ self.func("TO_TIMESTAMP", value, exp.Literal.string("YYYY-MM-DD HH24:MI:SS.FF6"))
139
+ if expression.name == "TIMESTAMP" and value.is_string
140
+ else self.sql(value)
141
+ )
142
+ return f"AS OF {expression.name} {value_sql}"
143
+ return super().version_sql(expression)
144
+
123
145
  def offset_sql(self, expression: exp.Offset) -> str:
124
146
  return f"{super().offset_sql(expression)} ROWS"
125
147
 
@@ -4,6 +4,7 @@ import typing as t
4
4
 
5
5
  from sqlglot import exp, generator, transforms
6
6
  from sqlglot.dialects.dialect import (
7
+ array_slice_to_slice_sql,
7
8
  bool_xor_sql,
8
9
  bracket_to_element_at_sql,
9
10
  datestrtodate_sql,
@@ -324,7 +325,7 @@ class PrestoGenerator(generator.Generator):
324
325
  exp.ArrayContains: rename_func("CONTAINS"),
325
326
  exp.ArrayToString: rename_func("ARRAY_JOIN"),
326
327
  exp.ArrayUniqueAgg: rename_func("SET_AGG"),
327
- exp.ArraySlice: rename_func("SLICE"),
328
+ exp.ArraySlice: array_slice_to_slice_sql,
328
329
  exp.AtTimeZone: rename_func("AT_TIMEZONE"),
329
330
  exp.BitwiseAnd: lambda self, e: self.func("BITWISE_AND", e.this, e.expression),
330
331
  exp.BitwiseLeftShift: rename_func("BITWISE_LEFT_SHIFT"),
@@ -4,6 +4,7 @@ import typing as t
4
4
 
5
5
  from sqlglot import exp, transforms
6
6
  from sqlglot.dialects.dialect import (
7
+ array_slice_to_slice_sql,
7
8
  bracket_to_element_at_sql,
8
9
  is_parse_json,
9
10
  rename_func,
@@ -139,7 +140,7 @@ class Spark2Generator(HiveGenerator):
139
140
  f"AGGREGATE({self.sql(e, 'this')}, 0, (acc, x) -> acc + x, acc -> acc)"
140
141
  ),
141
142
  exp.ArrayToString: rename_func("ARRAY_JOIN"),
142
- exp.ArraySlice: rename_func("SLICE"),
143
+ exp.ArraySlice: array_slice_to_slice_sql,
143
144
  exp.AtTimeZone: lambda self, e: self.func(
144
145
  "FROM_UTC_TIMESTAMP", e.this, e.args.get("zone")
145
146
  ),
@@ -79,6 +79,7 @@ class StarRocksGenerator(MySQLGenerator):
79
79
 
80
80
  TYPE_MAPPING = {
81
81
  **MySQLGenerator.TYPE_MAPPING,
82
+ exp.DType.HLLSKETCH: "HLL",
82
83
  exp.DType.INT128: "LARGEINT",
83
84
  exp.DType.TEXT: "STRING",
84
85
  exp.DType.TIMESTAMP: "DATETIME",
@@ -379,3 +380,23 @@ class StarRocksGenerator(MySQLGenerator):
379
380
  every = f" EVERY (INTERVAL {every} {unit})" if every and unit else ""
380
381
 
381
382
  return f"REFRESH{method}{kind}{starts}{every}"
383
+
384
+ def refresh_sql(self, expression: exp.Refresh) -> str:
385
+ this = self.sql(expression, "this")
386
+ kind = "" if isinstance(expression.this, exp.Literal) else f"{expression.text('kind')} "
387
+ partition_start = self.sql(expression, "partition_start")
388
+ partition_end = self.sql(expression, "partition_end")
389
+ partition = (
390
+ f" PARTITION START ({partition_start}) END ({partition_end})" if partition_start else ""
391
+ )
392
+ force = " FORCE" if expression.args.get("force") else ""
393
+ mode = self.sql(expression, "mode")
394
+ mode = f" WITH {mode} MODE" if mode else ""
395
+
396
+ return f"REFRESH {kind}{this}{partition}{force}{mode}"
397
+
398
+ def timeslice_sql(self, expression: exp.TimeSlice) -> str:
399
+ interval = exp.Interval(this=expression.expression, unit=expression.unit)
400
+ kind = expression.text("kind").upper()
401
+ boundary = exp.var("CEIL" if kind == "END" else "FLOOR") if kind else None
402
+ return self.func("TIME_SLICE", expression.this, interval, boundary)
@@ -10,6 +10,7 @@ from sqlglot.dialects.dialect import (
10
10
  strposition_sql,
11
11
  to_number_with_nls_param,
12
12
  )
13
+ from sqlglot.parsers.teradata import is_window_aggregate
13
14
  from collections import defaultdict
14
15
 
15
16
 
@@ -117,6 +118,24 @@ class TeradataGenerator(generator.Generator):
117
118
  ) -> str:
118
119
  return f"{self.sql(expression, 'this')} SAMPLE {self.expressions(expression)}"
119
120
 
121
+ def window_sql(self, expression: exp.Window) -> str:
122
+ spec = expression.args.get("spec")
123
+
124
+ if (
125
+ spec
126
+ and expression.args.get("order")
127
+ and is_window_aggregate(expression)
128
+ and spec.text("kind").upper() == "ROWS"
129
+ and spec.text("start").upper() == "UNBOUNDED"
130
+ and spec.text("start_side").upper() == "PRECEDING"
131
+ and spec.text("end").upper() == "UNBOUNDED"
132
+ and spec.text("end_side").upper() == "FOLLOWING"
133
+ ):
134
+ # This is the default frame of window aggregates with ORDER BY in Teradata
135
+ expression.set("spec", None)
136
+
137
+ return super().window_sql(expression)
138
+
120
139
  def partitionedbyproperty_sql(self, expression: exp.PartitionedByProperty) -> str:
121
140
  return f"PARTITION BY {self.sql(expression, 'this')}"
122
141