sqlglotc 30.11.0__tar.gz → 30.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. {sqlglotc-30.11.0/sqlglotc.egg-info → sqlglotc-30.13.0}/PKG-INFO +3 -3
  2. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/pyproject.toml +2 -2
  3. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/setup.py +7 -0
  4. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/aggregate.py +5 -4
  5. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/array.py +4 -0
  6. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/constraints.py +5 -0
  7. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/core.py +23 -10
  8. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/ddl.py +9 -1
  9. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/json.py +4 -0
  10. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/math.py +4 -0
  11. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/properties.py +5 -1
  12. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/query.py +2 -2
  13. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/string.py +11 -0
  14. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/temporal.py +19 -4
  15. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generator.py +91 -28
  16. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/bigquery.py +30 -0
  17. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/clickhouse.py +4 -1
  18. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/databricks.py +14 -0
  19. sqlglotc-30.13.0/sqlglot/generators/dax.py +7 -0
  20. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/dremio.py +8 -0
  21. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/drill.py +17 -1
  22. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/duckdb.py +174 -55
  23. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/exasol.py +1 -3
  24. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/hive.py +78 -27
  25. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/mysql.py +4 -0
  26. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/oracle.py +1 -0
  27. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/postgres.py +9 -3
  28. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/presto.py +66 -5
  29. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/snowflake.py +72 -2
  30. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/spark.py +0 -1
  31. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/spark2.py +23 -0
  32. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/sqlite.py +6 -1
  33. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/starrocks.py +15 -1
  34. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/trino.py +1 -0
  35. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/tsql.py +5 -2
  36. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/annotate_types.py +22 -8
  37. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/canonicalize_internal_names.py +19 -6
  38. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify_columns.py +105 -25
  39. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify_tables.py +7 -1
  40. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/resolver.py +27 -10
  41. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/scope.py +4 -2
  42. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/simplify.py +59 -27
  43. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parser.py +177 -80
  44. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/bigquery.py +40 -21
  45. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/clickhouse.py +15 -1
  46. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/databricks.py +16 -0
  47. sqlglotc-30.13.0/sqlglot/parsers/dax.py +48 -0
  48. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/drill.py +8 -0
  49. sqlglotc-30.13.0/sqlglot/parsers/druid.py +10 -0
  50. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/duckdb.py +4 -0
  51. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/exasol.py +4 -3
  52. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/hive.py +2 -18
  53. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/mysql.py +3 -0
  54. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/oracle.py +5 -2
  55. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/postgres.py +1 -0
  56. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/presto.py +6 -2
  57. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/snowflake.py +80 -7
  58. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/spark2.py +1 -1
  59. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/starrocks.py +4 -1
  60. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/tsql.py +10 -0
  61. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/time.py +23 -23
  62. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/tokenizer_core.py +14 -3
  63. {sqlglotc-30.11.0 → sqlglotc-30.13.0/sqlglotc.egg-info}/PKG-INFO +3 -3
  64. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/SOURCES.txt +4 -0
  65. sqlglotc-30.13.0/sqlglotc.egg-info/requires.txt +6 -0
  66. sqlglotc-30.13.0/sqlglotc.egg-info/scm_file_list.json +7 -0
  67. sqlglotc-30.13.0/sqlglotc.egg-info/scm_version.json +8 -0
  68. sqlglotc-30.11.0/sqlglot/parsers/druid.py +0 -7
  69. sqlglotc-30.11.0/sqlglotc.egg-info/requires.txt +0 -6
  70. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/MANIFEST.in +0 -0
  71. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/setup.cfg +0 -0
  72. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/errors.py +0 -0
  73. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/executor/table.py +0 -0
  74. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/builders.py +0 -0
  75. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/datatypes.py +0 -0
  76. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/dml.py +0 -0
  77. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/functions.py +0 -0
  78. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/athena.py +0 -0
  79. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/doris.py +0 -0
  80. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/druid.py +0 -0
  81. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/dune.py +0 -0
  82. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/fabric.py +0 -0
  83. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/materialize.py +0 -0
  84. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/prql.py +0 -0
  85. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/python.py +0 -0
  86. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/redshift.py +0 -0
  87. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/risingwave.py +0 -0
  88. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/singlestore.py +0 -0
  89. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/solr.py +0 -0
  90. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/tableau.py +0 -0
  91. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/teradata.py +0 -0
  92. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/helper.py +0 -0
  93. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/lineage.py +0 -0
  94. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  95. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  96. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify.py +0 -0
  97. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/athena.py +0 -0
  98. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/base.py +0 -0
  99. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/doris.py +0 -0
  100. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/dremio.py +0 -0
  101. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/dune.py +0 -0
  102. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/fabric.py +0 -0
  103. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/materialize.py +0 -0
  104. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/prql.py +0 -0
  105. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/redshift.py +0 -0
  106. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/risingwave.py +0 -0
  107. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/singlestore.py +0 -0
  108. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/solr.py +0 -0
  109. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/spark.py +0 -0
  110. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/sqlite.py +0 -0
  111. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/tableau.py +0 -0
  112. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/teradata.py +0 -0
  113. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/trino.py +0 -0
  114. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/schema.py +0 -0
  115. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/serde.py +0 -0
  116. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/trie.py +0 -0
  117. {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  118. {sqlglotc-30.11.0 → sqlglotc-30.13.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.11.0
3
+ Version: 30.13.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.11.0
10
+ Requires-Dist: sqlglot==30.13.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.1.0.post2; extra == "dev"
14
+ Requires-Dist: sqlglot-mypy>=2.1.0.post9; 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.1.0.post2"]
10
+ dev = ["setuptools >= 61.0", "setuptools_scm", "sqlglot-mypy >= 2.1.0.post9"]
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.1.0.post2",
20
+ "sqlglot-mypy >= 2.1.0.post9",
21
21
  "types-python-dateutil",
22
22
  "sqlglot",
23
23
  ]
@@ -90,6 +90,13 @@ def _source_paths():
90
90
 
91
91
 
92
92
  class build_ext(_build_ext):
93
+ def finalize_options(self):
94
+ super().finalize_options()
95
+ # Compile in parallel by default; builds driven by pip (wheel/sdist installs)
96
+ # have no way to pass -j. An explicit -j on the command line still wins.
97
+ if self.parallel is None:
98
+ self.parallel = os.cpu_count() or 1
99
+
93
100
  def copy_extensions_to_source(self):
94
101
  """For editable installs, put sqlglot.* .so files in the sqlglot source dir."""
95
102
  for ext in self.extensions:
@@ -102,6 +102,7 @@ class Count(Expression, AggFunc):
102
102
 
103
103
  class CountIf(Expression, AggFunc):
104
104
  _sql_names = ["COUNT_IF", "COUNTIF"]
105
+ arg_types = {"this": True, "zero_on_all_null": False}
105
106
 
106
107
 
107
108
  class CovarPop(Expression, AggFunc):
@@ -255,19 +256,19 @@ class RegrAvgy(Expression, AggFunc):
255
256
 
256
257
 
257
258
  class RegrCount(Expression, AggFunc):
258
- arg_types = {"this": True, "expression": True}
259
+ arg_types = {"this": True, "expression": False}
259
260
 
260
261
 
261
262
  class RegrIntercept(Expression, AggFunc):
262
- arg_types = {"this": True, "expression": True}
263
+ arg_types = {"this": True, "expression": False}
263
264
 
264
265
 
265
266
  class RegrR2(Expression, AggFunc):
266
- arg_types = {"this": True, "expression": True}
267
+ arg_types = {"this": True, "expression": False}
267
268
 
268
269
 
269
270
  class RegrSlope(Expression, AggFunc):
270
- arg_types = {"this": True, "expression": True}
271
+ arg_types = {"this": True, "expression": False}
271
272
 
272
273
 
273
274
  class RegrSxx(Expression, AggFunc):
@@ -114,6 +114,10 @@ class ArrayContainsAll(Expression, Binary, Func):
114
114
  _sql_names = ["ARRAY_CONTAINS_ALL", "ARRAY_HAS_ALL"]
115
115
 
116
116
 
117
+ class ArrayContainedBy(Expression, Binary, Func):
118
+ pass
119
+
120
+
117
121
  class ArrayExcept(Expression, Func):
118
122
  arg_types = {"this": True, "expression": True, "is_multiset": False}
119
123
 
@@ -220,6 +220,11 @@ class PrimaryKey(Expression):
220
220
  arg_types = {"this": False, "expressions": True, "options": False, "include": False}
221
221
 
222
222
 
223
+ # https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-ddl-create-table-constraint
224
+ class TimeseriesKey(Expression):
225
+ pass
226
+
227
+
223
228
  class IndexParameters(Expression):
224
229
  arg_types = {
225
230
  "using": False,
@@ -10,12 +10,14 @@ import re
10
10
  import sys
11
11
  import textwrap
12
12
  import typing as t
13
+ from builtins import type as Type
13
14
  from collections import deque
15
+ from collections.abc import Collection, Iterator, Mapping, MutableMapping, Sequence
14
16
  from copy import deepcopy
15
17
  from decimal import Decimal
16
18
  from functools import reduce
17
- from collections.abc import Iterator, Sequence, Collection, Mapping, MutableMapping
18
- from sqlglot._typing import E, T
19
+
20
+ from sqlglot._typing import E, GeneratorNoDialectArgs, ParserNoDialectArgs, T
19
21
  from sqlglot.errors import ParseError
20
22
  from sqlglot.helper import (
21
23
  camel_to_snake_case,
@@ -24,17 +26,15 @@ from sqlglot.helper import (
24
26
  to_bool,
25
27
  trait,
26
28
  )
27
-
28
29
  from sqlglot.tokenizer_core import Token
29
- from builtins import type as Type
30
- from sqlglot._typing import GeneratorNoDialectArgs, ParserNoDialectArgs
31
30
 
32
31
  if t.TYPE_CHECKING:
33
- from typing_extensions import Self, Unpack, Concatenate
32
+ from typing_extensions import Concatenate, Self, Unpack
33
+
34
+ from sqlglot._typing import P
34
35
  from sqlglot.dialects.dialect import DialectType
35
36
  from sqlglot.expressions.datatypes import DATA_TYPE, DataType, DType, Interval
36
37
  from sqlglot.expressions.query import Select
37
- from sqlglot._typing import P
38
38
 
39
39
  R = t.TypeVar("R")
40
40
 
@@ -1789,7 +1789,12 @@ class JoinHint(Expression):
1789
1789
 
1790
1790
 
1791
1791
  class Identifier(Expression):
1792
- arg_types = {"this": True, "quoted": False, "global_": False, "temporary": False}
1792
+ arg_types = {
1793
+ "this": True,
1794
+ "quoted": False,
1795
+ "global_": False,
1796
+ "temporary": False,
1797
+ }
1793
1798
  is_primitive = True
1794
1799
  _hash_raw_args = True
1795
1800
 
@@ -1802,12 +1807,19 @@ class Identifier(Expression):
1802
1807
  return self.name
1803
1808
 
1804
1809
 
1810
+ # https://docs.snowflake.com/en/sql-reference/identifier-literal
1811
+ # "expressions" holds the arguments when the resolved identifier is invoked as a
1812
+ # function, e.g. `IDENTIFIER('my_func')(1, 2)`
1813
+ class DynamicIdentifier(Expression, Func):
1814
+ arg_types = {"this": True, "expressions": False}
1815
+
1816
+
1805
1817
  class Opclass(Expression):
1806
1818
  arg_types = {"this": True, "expression": True}
1807
1819
 
1808
1820
 
1809
1821
  class Star(Expression):
1810
- arg_types = {"except_": False, "replace": False, "rename": False}
1822
+ arg_types = {"except_": False, "replace": False, "rename": False, "ilike": False}
1811
1823
 
1812
1824
  @property
1813
1825
  def name(self) -> str:
@@ -2415,7 +2427,8 @@ def convert(value: t.Any, copy: bool = False) -> Expr:
2415
2427
 
2416
2428
  return _Array(expressions=[convert(v, copy=copy) for v in value])
2417
2429
  if isinstance(value, dict):
2418
- from sqlglot.expressions.array import Array as _Array, Map as _Map
2430
+ from sqlglot.expressions.array import Array as _Array
2431
+ from sqlglot.expressions.array import Map as _Map
2419
2432
 
2420
2433
  return _Map(
2421
2434
  keys=_Array(expressions=[convert(k, copy=copy) for k in value]),
@@ -370,6 +370,14 @@ class DropPrimaryKey(Expression):
370
370
  arg_types = {}
371
371
 
372
372
 
373
+ class Undrop(Expression):
374
+ arg_types = {"this": True, "kind": True, "rename": False}
375
+
376
+ @property
377
+ def kind(self) -> str:
378
+ return self.args["kind"].upper()
379
+
380
+
373
381
  class Command(Expression):
374
382
  arg_types = {"this": True, "expression": False}
375
383
 
@@ -424,7 +432,7 @@ class NextValueFor(Expression, Func):
424
432
 
425
433
 
426
434
  class Execute(Expression):
427
- arg_types = {"this": True, "expressions": False}
435
+ arg_types = {"this": True, "expressions": False, "return_status": False}
428
436
 
429
437
  @property
430
438
  def name(self) -> str:
@@ -62,6 +62,10 @@ class JSONBDeleteAtPath(Expression, Binary, Func):
62
62
  pass
63
63
 
64
64
 
65
+ class JSONBPathExists(Expression, Binary, Predicate, Func):
66
+ pass
67
+
68
+
65
69
  class JSONBExists(Expression, Func):
66
70
  arg_types = {"this": True, "path": True}
67
71
  _sql_names = ["JSONB_EXISTS"]
@@ -168,6 +168,10 @@ class Pi(Expression, Func):
168
168
  arg_types = {}
169
169
 
170
170
 
171
+ class Rint(Expression, Func):
172
+ pass
173
+
174
+
171
175
  class Round(Expression, Func):
172
176
  arg_types = {
173
177
  "this": True,
@@ -5,8 +5,8 @@ from __future__ import annotations
5
5
  import typing as t
6
6
  from enum import auto
7
7
 
8
+ from sqlglot.expressions.core import ColumnConstraintKind, Expression, Literal, convert
8
9
  from sqlglot.helper import AutoName
9
- from sqlglot.expressions.core import Expression, ColumnConstraintKind, Literal, convert
10
10
 
11
11
 
12
12
  class Property(Expression):
@@ -63,6 +63,10 @@ class BlockCompressionProperty(Property):
63
63
  }
64
64
 
65
65
 
66
+ class CalledOnNullInputProperty(Property):
67
+ arg_types = {}
68
+
69
+
66
70
  class CatalogProperty(Property):
67
71
  arg_types = {}
68
72
 
@@ -1967,7 +1967,7 @@ class JSON(Expression):
1967
1967
 
1968
1968
 
1969
1969
  class JSONPath(Expression):
1970
- arg_types = {"expressions": True, "escape": False}
1970
+ arg_types = {"expressions": True}
1971
1971
 
1972
1972
  @property
1973
1973
  def output_name(self) -> str:
@@ -1984,7 +1984,7 @@ class JSONPathFilter(JSONPathPart):
1984
1984
 
1985
1985
 
1986
1986
  class JSONPathKey(JSONPathPart):
1987
- arg_types = {"this": True}
1987
+ arg_types = {"this": True, "quoted": False}
1988
1988
 
1989
1989
 
1990
1990
  class JSONPathRecursive(JSONPathPart):
@@ -477,6 +477,17 @@ class RegexpSplit(Expression, Func):
477
477
  arg_types = {"this": True, "expression": True, "limit": False}
478
478
 
479
479
 
480
+ class RegexpSubstr(Expression, Func):
481
+ arg_types = {
482
+ "this": True,
483
+ "expression": True,
484
+ "position": False,
485
+ "occurrence": False,
486
+ "parameters": False,
487
+ "group": False,
488
+ }
489
+
490
+
480
491
  # Hashing / cryptographic
481
492
 
482
493
 
@@ -106,7 +106,7 @@ class DatetimeAdd(Expression, Func, IntervalOp):
106
106
 
107
107
 
108
108
  class DatetimeDiff(Expression, Func, TimeUnit):
109
- arg_types = {"this": True, "expression": True, "unit": False}
109
+ arg_types = {"this": True, "expression": True, "unit": False, "date_part_boundary": False}
110
110
 
111
111
 
112
112
  class DatetimeSub(Expression, Func, IntervalOp):
@@ -437,8 +437,16 @@ class FromISO8601Timestamp(Expression, Func):
437
437
  _sql_names = ["FROM_ISO8601_TIMESTAMP"]
438
438
 
439
439
 
440
+ class FromISO8601Date(Expression, Func):
441
+ _sql_names = ["FROM_ISO8601_DATE"]
442
+
443
+
444
+ class FromISO8601TimestampNanos(Expression, Func):
445
+ _sql_names = ["FROM_ISO8601_TIMESTAMP_NANOS"]
446
+
447
+
440
448
  class ParseDatetime(Expression, Func):
441
- arg_types = {"this": True, "format": False, "zone": False}
449
+ arg_types = {"this": True, "format": False, "zone": False, "default_year": False}
442
450
 
443
451
 
444
452
  class ParseTime(Expression, Func):
@@ -446,11 +454,18 @@ class ParseTime(Expression, Func):
446
454
 
447
455
 
448
456
  class StrToDate(Expression, Func):
449
- arg_types = {"this": True, "format": False, "safe": False}
457
+ arg_types = {"this": True, "format": False, "safe": False, "default_year": False}
450
458
 
451
459
 
452
460
  class StrToTime(Expression, Func):
453
- arg_types = {"this": True, "format": True, "zone": False, "safe": False, "target_type": False}
461
+ arg_types = {
462
+ "this": True,
463
+ "format": True,
464
+ "zone": False,
465
+ "safe": False,
466
+ "target_type": False,
467
+ "default_year": False,
468
+ }
454
469
 
455
470
 
456
471
  class StrToUnix(Expression, Func):
@@ -9,6 +9,7 @@ from functools import reduce, wraps
9
9
  from sqlglot import exp
10
10
  from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
11
11
  from sqlglot.expressions import apply_index_offset
12
+ from sqlglot.expressions.core import maybe_parse
12
13
  from sqlglot.helper import csv, name_sequence, seq_get
13
14
  from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
14
15
  from sqlglot.time import format_time
@@ -139,6 +140,7 @@ class Generator:
139
140
  ),
140
141
  exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
141
142
  exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
143
+ exp.ArrayContainedBy: lambda self, e: self.binary(e, "<@"),
142
144
  exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
143
145
  exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
144
146
  exp.AssumeColumnConstraint: lambda self, e: f"ASSUME ({self.sql(e, 'this')})",
@@ -147,6 +149,7 @@ class Generator:
147
149
  exp.CaseSpecificColumnConstraint: lambda _, e: (
148
150
  f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC"
149
151
  ),
152
+ exp.CalledOnNullInputProperty: lambda *_: "CALLED ON NULL INPUT",
150
153
  exp.Ceil: lambda self, e: self.ceil_floor(e),
151
154
  exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
152
155
  exp.CharacterSetProperty: lambda self, e: (
@@ -203,6 +206,7 @@ class Generator:
203
206
  exp.JSONBContainsAnyTopKeys: lambda self, e: self.binary(e, "?|"),
204
207
  exp.JSONBContainsAllTopKeys: lambda self, e: self.binary(e, "?&"),
205
208
  exp.JSONBDeleteAtPath: lambda self, e: self.binary(e, "#-"),
209
+ exp.JSONBPathExists: lambda self, e: self.binary(e, "@?"),
206
210
  exp.JSONObject: lambda self, e: self._jsonobject_sql(e),
207
211
  exp.JSONObjectAgg: lambda self, e: self._jsonobject_sql(e),
208
212
  exp.LanguageProperty: lambda self, e: self.naked_property(e),
@@ -398,9 +402,6 @@ class Generator:
398
402
  # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
399
403
  UNNEST_WITH_ORDINALITY = True
400
404
 
401
- # Whether FILTER (WHERE cond) can be used for conditional aggregation
402
- AGGREGATE_FILTER_SUPPORTED = True
403
-
404
405
  # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
405
406
  SEMI_ANTI_JOIN_WITH_SIDE = True
406
407
 
@@ -425,6 +426,9 @@ class Generator:
425
426
  # The keyword to use when specifying the seed of a sample clause
426
427
  TABLESAMPLE_SEED_KEYWORD = "SEED"
427
428
 
429
+ # Whether the historical data clause (AT ... / BEFORE ...) is generated after the table alias
430
+ HISTORICAL_DATA_POST_ALIAS = False
431
+
428
432
  # Whether COLLATE is a function instead of a binary operator
429
433
  COLLATE_IS_FUNC = False
430
434
 
@@ -489,6 +493,12 @@ class Generator:
489
493
  # Whether to escape keys using single quotes in JSON paths
490
494
  JSON_PATH_SINGLE_QUOTE_ESCAPE = False
491
495
 
496
+ # Whether a quoted JSON path key (e.g. from a quoted identifier or ['key'] bracket) must be
497
+ # rendered in bracket form to preserve its case-sensitivity, even if it would otherwise match
498
+ # SAFE_JSON_PATH_KEY_RE and render as a bare dotted key. Needed for dialects like Databricks
499
+ # where a bare colon key is case-insensitive but a bracketed key is case-sensitive.
500
+ JSON_PATH_KEY_QUOTED_FORCES_BRACKETS = False
501
+
492
502
  # The JSONPathPart expressions supported by this dialect
493
503
  SUPPORTED_JSON_PATH_PARTS: t.ClassVar = ALL_JSON_PATH_PARTS.copy()
494
504
 
@@ -668,6 +678,7 @@ class Generator:
668
678
  exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
669
679
  exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
670
680
  exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
681
+ exp.CalledOnNullInputProperty: exp.Properties.Location.POST_SCHEMA,
671
682
  exp.CatalogProperty: exp.Properties.Location.POST_CREATE,
672
683
  exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
673
684
  exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
@@ -1007,23 +1018,24 @@ class Generator:
1007
1018
  if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
1008
1019
  return sql
1009
1020
 
1010
- comments_sql = " ".join(
1011
- f"/*{self.sanitize_comment(comment)}*/" for comment in comments if comment
1012
- )
1021
+ comments_list = [
1022
+ f"/*{self._replace_line_breaks(self.sanitize_comment(comment))}*/"
1023
+ for comment in comments
1024
+ if comment
1025
+ ]
1013
1026
 
1014
- if not comments_sql:
1027
+ if not comments_list:
1015
1028
  return sql
1016
1029
 
1017
- comments_sql = self._replace_line_breaks(comments_sql)
1018
-
1019
1030
  if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
1031
+ comments_sql = self.sep().join(comments_list)
1020
1032
  return (
1021
1033
  f"{self.sep()}{comments_sql}{sql}"
1022
1034
  if not sql or sql[0].isspace()
1023
1035
  else f"{comments_sql}{self.sep()}{sql}"
1024
1036
  )
1025
1037
 
1026
- return f"{sql} {comments_sql}"
1038
+ return f"{sql} {' '.join(comments_list)}"
1027
1039
 
1028
1040
  def wrap(self, expression: exp.Expr | str) -> str:
1029
1041
  this_sql = (
@@ -1893,16 +1905,9 @@ class Generator:
1893
1905
  return f"{percent}{rows}{with_ties}"
1894
1906
 
1895
1907
  def filter_sql(self, expression: exp.Filter) -> str:
1896
- if self.AGGREGATE_FILTER_SUPPORTED:
1897
- this = self.sql(expression, "this")
1898
- where = self.sql(expression, "expression").strip()
1899
- return f"{this} FILTER({where})"
1900
-
1901
- agg = expression.this
1902
- agg_arg = agg.this
1903
- cond = expression.expression.this
1904
- agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1905
- return self.sql(agg)
1908
+ this = self.sql(expression, "this")
1909
+ where = self.sql(expression, "expression").strip()
1910
+ return f"{this} FILTER({where})"
1906
1911
 
1907
1912
  def hint_sql(self, expression: exp.Hint) -> str:
1908
1913
  if not self.QUERY_HINTS:
@@ -1945,6 +1950,20 @@ class Generator:
1945
1950
  params = self.sql(expression, "params")
1946
1951
  return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1947
1952
 
1953
+ def dynamicidentifier_sql(self, expression: exp.DynamicIdentifier) -> str:
1954
+ this = expression.this
1955
+ if this and this.is_string:
1956
+ resolved = maybe_parse(this.name).sql(self.dialect)
1957
+ if "expressions" in expression.args:
1958
+ # `IDENTIFIER(...)` invoked as a function, e.g. `IDENTIFIER('my_func')(1, 2)`
1959
+ # We can't safely emit the call to other dialects since name/arg semantics may differ
1960
+ self.unsupported(
1961
+ "Transpiling dynamically-invoked IDENTIFIER() functions is unsupported"
1962
+ )
1963
+ return resolved
1964
+ self.unsupported("IDENTIFIER() with non-literal arguments is not supported")
1965
+ return self.func("IDENTIFIER", this)
1966
+
1948
1967
  def identifier_sql(self, expression: exp.Identifier) -> str:
1949
1968
  text = expression.name
1950
1969
  lower = text.lower()
@@ -2393,10 +2412,12 @@ class Generator:
2393
2412
  laterals = self.expressions(expression, key="laterals", sep="")
2394
2413
 
2395
2414
  file_format = self.sql(expression, "format")
2415
+ pattern = self.sql(expression, "pattern")
2396
2416
  if file_format:
2397
- pattern = self.sql(expression, "pattern")
2398
2417
  pattern = f", PATTERN => {pattern}" if pattern else ""
2399
2418
  file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2419
+ elif pattern:
2420
+ file_format = f" (PATTERN => {pattern})"
2400
2421
 
2401
2422
  ordinality = expression.args.get("ordinality") or ""
2402
2423
  if ordinality:
@@ -2405,7 +2426,10 @@ class Generator:
2405
2426
 
2406
2427
  when = self.sql(expression, "when")
2407
2428
  if when:
2408
- table = f"{table} {when}"
2429
+ if self.HISTORICAL_DATA_POST_ALIAS:
2430
+ alias = f"{alias} {when}"
2431
+ else:
2432
+ table = f"{table} {when}"
2409
2433
 
2410
2434
  changes = self.sql(expression, "changes")
2411
2435
  changes = f" {changes}" if changes else ""
@@ -3215,7 +3239,12 @@ class Generator:
3215
3239
  limit = expression.args.get("limit")
3216
3240
 
3217
3241
  if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
3218
- limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
3242
+ count = limit.args.get("count")
3243
+ # "FETCH FIRST ROWS ONLY" without a count means one row per the SQL
3244
+ # standard; emitting a bare "LIMIT" here would produce invalid SQL.
3245
+ limit = exp.Limit(
3246
+ expression=exp.maybe_copy(count) if count is not None else exp.Literal.number(1)
3247
+ )
3219
3248
  elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
3220
3249
  limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
3221
3250
 
@@ -3371,7 +3400,9 @@ class Generator:
3371
3400
  replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
3372
3401
  rename = self.expressions(expression, key="rename", flat=True)
3373
3402
  rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
3374
- return f"*{except_}{replace}{rename}"
3403
+ ilike = self.sql(expression, "ilike")
3404
+ ilike = f"{self.seg('ILIKE')} {ilike}" if ilike else ""
3405
+ return f"*{ilike}{except_}{replace}{rename}"
3375
3406
 
3376
3407
  def parameter_sql(self, expression: exp.Parameter) -> str:
3377
3408
  this = self.sql(expression, "this")
@@ -3693,6 +3724,10 @@ class Generator:
3693
3724
  options = f" {options}" if options else ""
3694
3725
  return f"PRIMARY KEY{this} ({expressions}){include}{options}"
3695
3726
 
3727
+ def timeserieskey_sql(self, expression: exp.TimeseriesKey) -> str:
3728
+ self.unsupported("TIMESERIES primary key columns are not supported")
3729
+ return self.sql(expression, "this")
3730
+
3696
3731
  def if_sql(self, expression: exp.If) -> str:
3697
3732
  return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
3698
3733
 
@@ -3719,9 +3754,6 @@ class Generator:
3719
3754
  def jsonpath_sql(self, expression: exp.JSONPath) -> str:
3720
3755
  path = self.expressions(expression, sep="", flat=True).lstrip(".")
3721
3756
 
3722
- if expression.args.get("escape"):
3723
- path = self.escape_str(path)
3724
-
3725
3757
  if self.QUOTE_JSON_PATH:
3726
3758
  path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
3727
3759
 
@@ -3975,6 +4007,15 @@ class Generator:
3975
4007
  zone = self.sql(expression, "zone")
3976
4008
  return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
3977
4009
 
4010
+ def fromiso8601date_sql(self, expression: exp.FromISO8601Date) -> str:
4011
+ return self.sql(exp.cast(expression.this, exp.DType.DATE))
4012
+
4013
+ def fromiso8601timestamp_sql(self, expression: exp.FromISO8601Timestamp) -> str:
4014
+ return self.sql(exp.cast(expression.this, exp.DType.TIMESTAMPTZ))
4015
+
4016
+ def fromiso8601timestampnanos_sql(self, expression: exp.FromISO8601TimestampNanos) -> str:
4017
+ return self.sql(exp.cast(expression.this, exp.DType.TIMESTAMPTZ))
4018
+
3978
4019
  def add_sql(self, expression: exp.Add) -> str:
3979
4020
  return self.binary(expression, "+")
3980
4021
 
@@ -4054,6 +4095,18 @@ class Generator:
4054
4095
  def strtotime_sql(self, expression: exp.StrToTime) -> str:
4055
4096
  return self.func("STR_TO_TIME", expression.this, expression.args.get("format"))
4056
4097
 
4098
+ # Base implementation that excludes the safe and default_year metadata args
4099
+ def strtodate_sql(self, expression: exp.StrToDate) -> str:
4100
+ return self.func("STR_TO_DATE", expression.this, expression.args.get("format"))
4101
+
4102
+ def parsedatetime_sql(self, expression: exp.ParseDatetime) -> str:
4103
+ return self.func(
4104
+ "PARSE_DATETIME",
4105
+ expression.this,
4106
+ expression.args.get("format"),
4107
+ expression.args.get("zone"),
4108
+ )
4109
+
4057
4110
  def currentdate_sql(self, expression: exp.CurrentDate) -> str:
4058
4111
  zone = self.sql(expression, "this")
4059
4112
  return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
@@ -5228,10 +5281,20 @@ class Generator:
5228
5281
  this = self.json_path_part(this)
5229
5282
  return f".{this}" if this else ""
5230
5283
 
5231
- if self.SAFE_JSON_PATH_KEY_RE.match(this):
5284
+ quoted = expression.args.get("quoted")
5285
+ if not (
5286
+ quoted and self.JSON_PATH_KEY_QUOTED_FORCES_BRACKETS
5287
+ ) and self.SAFE_JSON_PATH_KEY_RE.match(this):
5232
5288
  return f".{this}"
5233
5289
 
5234
5290
  this = self.json_path_part(this)
5291
+
5292
+ if quoted and self.QUOTE_JSON_PATH:
5293
+ # The whole path is rendered as a single quoted string literal, so the bracketed key
5294
+ # (which may itself contain backslash-escaped quotes, e.g. ["x \"y\"z"]) must be
5295
+ # escaped again for the outer string literal (-> ["x \\"y\\"z"]).
5296
+ this = self.escape_str(this)
5297
+
5235
5298
  return (
5236
5299
  f"[{this}]"
5237
5300
  if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
@@ -137,6 +137,34 @@ def _pushdown_cte_column_names(expression: exp.Expr) -> exp.Expr:
137
137
  return expression
138
138
 
139
139
 
140
+ def _unnest_explode_generate_series(expression: exp.Expr) -> exp.Expr:
141
+ """
142
+ Rewrites exploding GENERATE_SERIES projections into table references, e.g.
143
+
144
+ SELECT GENERATE_SERIES(1, 2) AS x -> SELECT x FROM GENERATE_SERIES(1, 2) AS x
145
+ SELECT y, GENERATE_SERIES(1, 2) AS x FROM t -> SELECT y, x FROM t CROSS JOIN GENERATE_SERIES(1, 2) AS x
146
+
147
+ since BigQuery can't explode in the projection and must unnest it in the FROM clause instead.
148
+ The resulting table reference is unnested downstream by `transforms.unnest_generate_series`.
149
+ """
150
+ if isinstance(expression, exp.Select):
151
+ for projection in expression.selects:
152
+ if isinstance(series := projection.unalias(), exp.ExplodingGenerateSeries):
153
+ column_name = projection.output_name or "_gen_series_value"
154
+
155
+ projection.replace(exp.column(column_name))
156
+ table = exp.Table(
157
+ this=series, alias=exp.TableAlias(this=exp.to_identifier(column_name))
158
+ )
159
+
160
+ if expression.args.get("from_"):
161
+ expression.join(table, copy=False, join_type="CROSS")
162
+ else:
163
+ expression.set("from_", exp.From(this=table))
164
+
165
+ return expression
166
+
167
+
140
168
  def _array_contains_sql(self: BigQueryGenerator, expression: exp.ArrayContains) -> str:
141
169
  return self.sql(
142
170
  exp.Exists(
@@ -358,6 +386,7 @@ class BigQueryGenerator(generator.Generator):
358
386
  exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this),
359
387
  exp.Select: transforms.preprocess(
360
388
  [
389
+ _unnest_explode_generate_series,
361
390
  transforms.explode_projection_to_unnest(),
362
391
  transforms.unqualify_unnest,
363
392
  transforms.eliminate_distinct_on,
@@ -379,6 +408,7 @@ class BigQueryGenerator(generator.Generator):
379
408
  exp.StrToDate: _str_to_datetime_sql,
380
409
  exp.StrToTime: _str_to_datetime_sql,
381
410
  exp.SessionUser: lambda *_: "SESSION_USER()",
411
+ exp.Table: transforms.preprocess([transforms.unnest_generate_series]),
382
412
  exp.TimeAdd: date_add_interval_sql("TIME", "ADD"),
383
413
  exp.TimeFromParts: rename_func("TIME"),
384
414
  exp.TimestampFromParts: rename_func("DATETIME"),