sqlglotc 30.12.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.12.0/sqlglotc.egg-info → sqlglotc-30.13.0}/PKG-INFO +3 -3
  2. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/pyproject.toml +2 -2
  3. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/setup.py +7 -0
  4. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/aggregate.py +5 -4
  5. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/constraints.py +5 -0
  6. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/ddl.py +1 -1
  7. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/math.py +4 -0
  8. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/string.py +11 -0
  9. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/temporal.py +14 -3
  10. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generator.py +27 -15
  11. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/bigquery.py +30 -0
  12. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/databricks.py +13 -0
  13. sqlglotc-30.13.0/sqlglot/generators/dax.py +7 -0
  14. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/dremio.py +8 -0
  15. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/drill.py +17 -1
  16. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/duckdb.py +29 -59
  17. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/hive.py +78 -27
  18. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/oracle.py +1 -0
  19. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/postgres.py +8 -2
  20. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/presto.py +64 -5
  21. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/snowflake.py +57 -1
  22. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/spark.py +0 -1
  23. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/spark2.py +23 -0
  24. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/sqlite.py +6 -1
  25. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/trino.py +1 -0
  26. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/tsql.py +5 -2
  27. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/annotate_types.py +22 -8
  28. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/canonicalize_internal_names.py +19 -6
  29. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify_columns.py +84 -22
  30. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/resolver.py +23 -10
  31. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/scope.py +4 -2
  32. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/simplify.py +2 -5
  33. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parser.py +114 -51
  34. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/bigquery.py +32 -18
  35. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/clickhouse.py +10 -1
  36. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/databricks.py +15 -0
  37. sqlglotc-30.13.0/sqlglot/parsers/dax.py +48 -0
  38. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/drill.py +8 -0
  39. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/duckdb.py +4 -0
  40. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/exasol.py +1 -1
  41. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/hive.py +2 -18
  42. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/mysql.py +3 -0
  43. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/oracle.py +5 -2
  44. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/presto.py +6 -2
  45. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/snowflake.py +5 -4
  46. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/spark2.py +1 -1
  47. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/tsql.py +10 -0
  48. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/time.py +23 -23
  49. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/tokenizer_core.py +12 -3
  50. {sqlglotc-30.12.0 → sqlglotc-30.13.0/sqlglotc.egg-info}/PKG-INFO +3 -3
  51. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/SOURCES.txt +2 -0
  52. sqlglotc-30.13.0/sqlglotc.egg-info/requires.txt +6 -0
  53. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/scm_file_list.json +2 -2
  54. sqlglotc-30.13.0/sqlglotc.egg-info/scm_version.json +8 -0
  55. sqlglotc-30.12.0/sqlglotc.egg-info/requires.txt +0 -6
  56. sqlglotc-30.12.0/sqlglotc.egg-info/scm_version.json +0 -8
  57. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/MANIFEST.in +0 -0
  58. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/setup.cfg +0 -0
  59. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/errors.py +0 -0
  60. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/executor/table.py +0 -0
  61. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/array.py +0 -0
  62. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/builders.py +0 -0
  63. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/core.py +0 -0
  64. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/datatypes.py +0 -0
  65. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/dml.py +0 -0
  66. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/functions.py +0 -0
  67. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/json.py +0 -0
  68. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/properties.py +0 -0
  69. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/expressions/query.py +0 -0
  70. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/athena.py +0 -0
  71. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/clickhouse.py +0 -0
  72. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/doris.py +0 -0
  73. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/druid.py +0 -0
  74. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/dune.py +0 -0
  75. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/exasol.py +0 -0
  76. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/fabric.py +0 -0
  77. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/materialize.py +0 -0
  78. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/mysql.py +0 -0
  79. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/prql.py +0 -0
  80. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/python.py +0 -0
  81. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/redshift.py +0 -0
  82. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/risingwave.py +0 -0
  83. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/singlestore.py +0 -0
  84. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/solr.py +0 -0
  85. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/starrocks.py +0 -0
  86. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/tableau.py +0 -0
  87. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/generators/teradata.py +0 -0
  88. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/helper.py +0 -0
  89. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/lineage.py +0 -0
  90. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
  91. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
  92. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify.py +0 -0
  93. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify_tables.py +0 -0
  94. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/athena.py +0 -0
  95. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/base.py +0 -0
  96. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/doris.py +0 -0
  97. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/dremio.py +0 -0
  98. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/druid.py +0 -0
  99. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/dune.py +0 -0
  100. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/fabric.py +0 -0
  101. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/materialize.py +0 -0
  102. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/postgres.py +0 -0
  103. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/prql.py +0 -0
  104. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/redshift.py +0 -0
  105. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/risingwave.py +0 -0
  106. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/singlestore.py +0 -0
  107. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/solr.py +0 -0
  108. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/spark.py +0 -0
  109. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/sqlite.py +0 -0
  110. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/starrocks.py +0 -0
  111. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/tableau.py +0 -0
  112. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/teradata.py +0 -0
  113. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/parsers/trino.py +0 -0
  114. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/schema.py +0 -0
  115. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/serde.py +0 -0
  116. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglot/trie.py +0 -0
  117. {sqlglotc-30.12.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
  118. {sqlglotc-30.12.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.12.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.12.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.post3; 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.post3"]
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.post3",
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):
@@ -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,
@@ -432,7 +432,7 @@ class NextValueFor(Expression, Func):
432
432
 
433
433
 
434
434
  class Execute(Expression):
435
- arg_types = {"this": True, "expressions": False}
435
+ arg_types = {"this": True, "expressions": False, "return_status": False}
436
436
 
437
437
  @property
438
438
  def name(self) -> str:
@@ -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,
@@ -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):
@@ -441,6 +441,10 @@ class FromISO8601Date(Expression, Func):
441
441
  _sql_names = ["FROM_ISO8601_DATE"]
442
442
 
443
443
 
444
+ class FromISO8601TimestampNanos(Expression, Func):
445
+ _sql_names = ["FROM_ISO8601_TIMESTAMP_NANOS"]
446
+
447
+
444
448
  class ParseDatetime(Expression, Func):
445
449
  arg_types = {"this": True, "format": False, "zone": False, "default_year": False}
446
450
 
@@ -450,11 +454,18 @@ class ParseTime(Expression, Func):
450
454
 
451
455
 
452
456
  class StrToDate(Expression, Func):
453
- arg_types = {"this": True, "format": False, "safe": False}
457
+ arg_types = {"this": True, "format": False, "safe": False, "default_year": False}
454
458
 
455
459
 
456
460
  class StrToTime(Expression, Func):
457
- 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
+ }
458
469
 
459
470
 
460
471
  class StrToUnix(Expression, Func):
@@ -402,9 +402,6 @@ class Generator:
402
402
  # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
403
403
  UNNEST_WITH_ORDINALITY = True
404
404
 
405
- # Whether FILTER (WHERE cond) can be used for conditional aggregation
406
- AGGREGATE_FILTER_SUPPORTED = True
407
-
408
405
  # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
409
406
  SEMI_ANTI_JOIN_WITH_SIDE = True
410
407
 
@@ -429,6 +426,9 @@ class Generator:
429
426
  # The keyword to use when specifying the seed of a sample clause
430
427
  TABLESAMPLE_SEED_KEYWORD = "SEED"
431
428
 
429
+ # Whether the historical data clause (AT ... / BEFORE ...) is generated after the table alias
430
+ HISTORICAL_DATA_POST_ALIAS = False
431
+
432
432
  # Whether COLLATE is a function instead of a binary operator
433
433
  COLLATE_IS_FUNC = False
434
434
 
@@ -1905,16 +1905,9 @@ class Generator:
1905
1905
  return f"{percent}{rows}{with_ties}"
1906
1906
 
1907
1907
  def filter_sql(self, expression: exp.Filter) -> str:
1908
- if self.AGGREGATE_FILTER_SUPPORTED:
1909
- this = self.sql(expression, "this")
1910
- where = self.sql(expression, "expression").strip()
1911
- return f"{this} FILTER({where})"
1912
-
1913
- agg = expression.this
1914
- agg_arg = agg.this
1915
- cond = expression.expression.this
1916
- agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1917
- return self.sql(agg)
1908
+ this = self.sql(expression, "this")
1909
+ where = self.sql(expression, "expression").strip()
1910
+ return f"{this} FILTER({where})"
1918
1911
 
1919
1912
  def hint_sql(self, expression: exp.Hint) -> str:
1920
1913
  if not self.QUERY_HINTS:
@@ -2433,7 +2426,10 @@ class Generator:
2433
2426
 
2434
2427
  when = self.sql(expression, "when")
2435
2428
  if when:
2436
- table = f"{table} {when}"
2429
+ if self.HISTORICAL_DATA_POST_ALIAS:
2430
+ alias = f"{alias} {when}"
2431
+ else:
2432
+ table = f"{table} {when}"
2437
2433
 
2438
2434
  changes = self.sql(expression, "changes")
2439
2435
  changes = f" {changes}" if changes else ""
@@ -3243,7 +3239,12 @@ class Generator:
3243
3239
  limit = expression.args.get("limit")
3244
3240
 
3245
3241
  if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
3246
- 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
+ )
3247
3248
  elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
3248
3249
  limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
3249
3250
 
@@ -3723,6 +3724,10 @@ class Generator:
3723
3724
  options = f" {options}" if options else ""
3724
3725
  return f"PRIMARY KEY{this} ({expressions}){include}{options}"
3725
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
+
3726
3731
  def if_sql(self, expression: exp.If) -> str:
3727
3732
  return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
3728
3733
 
@@ -4008,6 +4013,9 @@ class Generator:
4008
4013
  def fromiso8601timestamp_sql(self, expression: exp.FromISO8601Timestamp) -> str:
4009
4014
  return self.sql(exp.cast(expression.this, exp.DType.TIMESTAMPTZ))
4010
4015
 
4016
+ def fromiso8601timestampnanos_sql(self, expression: exp.FromISO8601TimestampNanos) -> str:
4017
+ return self.sql(exp.cast(expression.this, exp.DType.TIMESTAMPTZ))
4018
+
4011
4019
  def add_sql(self, expression: exp.Add) -> str:
4012
4020
  return self.binary(expression, "+")
4013
4021
 
@@ -4087,6 +4095,10 @@ class Generator:
4087
4095
  def strtotime_sql(self, expression: exp.StrToTime) -> str:
4088
4096
  return self.func("STR_TO_TIME", expression.this, expression.args.get("format"))
4089
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
+
4090
4102
  def parsedatetime_sql(self, expression: exp.ParseDatetime) -> str:
4091
4103
  return self.func(
4092
4104
  "PARSE_DATETIME",
@@ -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"),
@@ -57,6 +57,9 @@ class DatabricksGenerator(SparkGenerator):
57
57
  exp.CurrentCatalog: lambda *_: "CURRENT_CATALOG()",
58
58
  exp.RegexpLike: None,
59
59
  exp.TryCast: None,
60
+ exp.RegrAvgx: lambda self, e: self._regr_sql(e),
61
+ exp.RegrSxx: lambda self, e: self._regr_sql(e),
62
+ exp.RegrSyy: lambda self, e: self._regr_sql(e),
60
63
  }.items()
61
64
  if v is not None
62
65
  }
@@ -90,6 +93,9 @@ class DatabricksGenerator(SparkGenerator):
90
93
 
91
94
  return super().columndef_sql(expression, sep)
92
95
 
96
+ def timeserieskey_sql(self, expression: exp.TimeseriesKey) -> str:
97
+ return f"{self.sql(expression, 'this')} TIMESERIES"
98
+
93
99
  def jsonpath_sql(self, expression: exp.JSONPath) -> str:
94
100
  expression.set("escape", None)
95
101
  path = super().jsonpath_sql(expression)
@@ -109,6 +115,13 @@ class DatabricksGenerator(SparkGenerator):
109
115
 
110
116
  return self.func("UNIFORM", expression.this, expression.expression, seed)
111
117
 
118
+ def _regr_sql(self, expression: exp.RegrAvgx | exp.RegrSxx | exp.RegrSyy) -> str:
119
+ name = expression.sql_name()
120
+ x = expression.expression
121
+ if isinstance(x, exp.Distinct):
122
+ return self.func(name, exp.Distinct(expressions=[expression.this]), *x.expressions)
123
+ return self.func(name, expression.this, x)
124
+
112
125
  def clusterproperty_sql(self, expression):
113
126
  this = self.sql(expression, "this") or f"({self.expressions(expression, flat=True)})"
114
127
  return f"CLUSTER BY {this}"
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from sqlglot import generator
4
+
5
+
6
+ class DAXGenerator(generator.Generator):
7
+ AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
@@ -72,6 +72,14 @@ class DremioGenerator(generator.Generator):
72
72
  exp.GenerateSeries: rename_func("ARRAY_GENERATE_RANGE"),
73
73
  }
74
74
 
75
+ def version_sql(self, expression: exp.Version) -> str:
76
+ if expression.text("kind") == "AS OF":
77
+ this = "SNAPSHOT" if expression.name == "VERSION" else expression.name
78
+ return f"AT {this} {self.sql(expression, 'expression')}"
79
+
80
+ self.unsupported("Range time travel is not supported in Dremio")
81
+ return super().version_sql(expression)
82
+
75
83
  def datatype_sql(self, expression: exp.DataType) -> str:
76
84
  """
77
85
  Reject time-zone-aware TIMESTAMPs, which Dremio does not accept
@@ -29,6 +29,7 @@ class DrillGenerator(generator.Generator):
29
29
  TRY_SUPPORTED = False
30
30
  SUPPORTS_UESCAPE = False
31
31
  SUPPORTS_DECODE_CASE = False
32
+ SUPPORTS_LIKE_QUANTIFIERS = False
32
33
 
33
34
  AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
34
35
 
@@ -76,7 +77,6 @@ class DrillGenerator(generator.Generator):
76
77
  exp.If: lambda self, e: (
77
78
  f"`IF`({self.format_args(e.this, e.args.get('true'), e.args.get('false'))})"
78
79
  ),
79
- exp.ILike: lambda self, e: self.binary(e, "`ILIKE`"),
80
80
  exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
81
81
  rename_func("LEVENSHTEIN_DISTANCE")
82
82
  ),
@@ -104,6 +104,22 @@ class DrillGenerator(generator.Generator):
104
104
  ),
105
105
  }
106
106
 
107
+ def ilike_sql(self, expression: exp.ILike) -> str:
108
+ if isinstance(expression.expression, (exp.All, exp.Any)):
109
+ return super().ilike_sql(expression)
110
+ return self._ilike_func_sql(expression)
111
+
112
+ def escape_sql(self, expression: exp.Escape) -> str:
113
+ # Drill takes the escape character as ILIKE's third argument instead of an ESCAPE clause
114
+ this = expression.this
115
+ if isinstance(this, exp.ILike) and not isinstance(this.expression, (exp.All, exp.Any)):
116
+ return self._ilike_func_sql(this, expression.expression)
117
+ return super().escape_sql(expression)
118
+
119
+ def _ilike_func_sql(self, expression: exp.ILike, escape: exp.Expr | None = None) -> str:
120
+ ilike = self.func("ILIKE", expression.this, expression.expression, escape)
121
+ return f"NOT {ilike}" if expression.args.get("negate") else ilike
122
+
107
123
 
108
124
  def _drill_dateint_format() -> str:
109
125
  from sqlglot.dialects.drill import Drill
@@ -32,9 +32,10 @@ from sqlglot.dialects.dialect import (
32
32
  rename_func,
33
33
  remove_from_array_using_filter,
34
34
  strposition_sql,
35
- str_to_time_sql,
36
35
  timestrtotime_sql,
37
36
  unit_to_str,
37
+ week_unit_to_dow,
38
+ WEEK_START_DAY_TO_DOW,
38
39
  )
39
40
  from sqlglot.generator import unsupported_args
40
41
  from sqlglot.helper import find_new_name, is_date_unit, seq_get
@@ -69,18 +70,6 @@ WS_CONTROL_CHARS_TO_DUCK = {
69
70
  "\u001f": 31,
70
71
  }
71
72
 
72
- # Days of week to ISO 8601 day-of-week numbers
73
- # ISO 8601 standard: Monday=1, Tuesday=2, Wednesday=3, Thursday=4, Friday=5, Saturday=6, Sunday=7
74
- WEEK_START_DAY_TO_DOW = {
75
- "MONDAY": 1,
76
- "TUESDAY": 2,
77
- "WEDNESDAY": 3,
78
- "THURSDAY": 4,
79
- "FRIDAY": 5,
80
- "SATURDAY": 6,
81
- "SUNDAY": 7,
82
- }
83
-
84
73
  MAX_BIT_POSITION = exp.Literal.number(32768)
85
74
 
86
75
  # cs/as/ps are Snowflake defaults; DuckDB already behaves the same way, so they are safe to drop.
@@ -906,33 +895,6 @@ def _implicit_datetime_cast(
906
895
  return arg
907
896
 
908
897
 
909
- def _week_unit_to_dow(unit: exp.Expr | None) -> int | None:
910
- """
911
- Compute the Monday-based day shift to align DATE_DIFF('WEEK', ...) coming
912
- from other dialects, e.g BigQuery's WEEK(<day>) or ISOWEEK unit parts.
913
-
914
- Args:
915
- unit: The unit expression (Var for ISOWEEK or WeekStart)
916
-
917
- Returns:
918
- The ISO 8601 day number (Monday=1, Sunday=7 etc) or None if not a week unit or if day is dynamic (not a constant).
919
-
920
- Examples:
921
- "WEEK(SUNDAY)" -> 7
922
- "WEEK(MONDAY)" -> 1
923
- "ISOWEEK" -> 1
924
- """
925
- # Handle plain Var expressions for ISOWEEK only
926
- if isinstance(unit, exp.Var) and unit.name.upper() in "ISOWEEK":
927
- return 1
928
-
929
- # Handle WeekStart expressions with explicit day
930
- if isinstance(unit, exp.WeekStart):
931
- return WEEK_START_DAY_TO_DOW.get(unit.name.upper())
932
-
933
- return None
934
-
935
-
936
898
  def _build_week_trunc_expression(
937
899
  date_expr: exp.Expr,
938
900
  start_dow: int,
@@ -989,7 +951,7 @@ def _date_diff_sql(self: DuckDBGenerator, expression: exp.DateDiff | exp.Datetim
989
951
  date_part_boundary = expression.args.get("date_part_boundary")
990
952
 
991
953
  # Extract week start day; returns None if day is dynamic (column/placeholder)
992
- week_start = _week_unit_to_dow(unit)
954
+ week_start = week_unit_to_dow(unit)
993
955
  if date_part_boundary and week_start and this and expr:
994
956
  expression.set("unit", exp.Literal.string("WEEK"))
995
957
 
@@ -1606,6 +1568,7 @@ class DuckDBGenerator(generator.Generator):
1606
1568
  ARRAY_SIZE_DIM_REQUIRED: bool | None = False
1607
1569
  NORMALIZE_EXTRACT_DATE_PARTS = True
1608
1570
  SUPPORTS_LIKE_QUANTIFIERS = False
1571
+ HISTORICAL_DATA_POST_ALIAS = True
1609
1572
  SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD = True
1610
1573
 
1611
1574
  TRANSFORMS = {
@@ -2751,14 +2714,13 @@ class DuckDBGenerator(generator.Generator):
2751
2714
  exp.DType.TIMESTAMPTZ,
2752
2715
  )
2753
2716
 
2717
+ value, formatted_time = self._strptime_default_year(expression)
2718
+
2754
2719
  if expression.args.get("safe"):
2755
- formatted_time = self.format_time(expression)
2756
2720
  cast_type = exp.DType.TIMESTAMPTZ if needs_tz else exp.DType.TIMESTAMP
2757
- return self.sql(
2758
- exp.cast(self.func("TRY_STRPTIME", expression.this, formatted_time), cast_type)
2759
- )
2721
+ return self.sql(exp.cast(self.func("TRY_STRPTIME", value, formatted_time), cast_type))
2760
2722
 
2761
- base_sql = str_to_time_sql(self, expression)
2723
+ base_sql = self.func("STRPTIME", value, formatted_time)
2762
2724
  if needs_tz:
2763
2725
  return self.sql(
2764
2726
  exp.cast(
@@ -2769,27 +2731,30 @@ class DuckDBGenerator(generator.Generator):
2769
2731
  return base_sql
2770
2732
 
2771
2733
  def strtodate_sql(self, expression: exp.StrToDate) -> str:
2772
- formatted_time = self.format_time(expression)
2734
+ value, formatted_time = self._strptime_default_year(expression)
2773
2735
  function_name = "STRPTIME" if not expression.args.get("safe") else "TRY_STRPTIME"
2774
2736
  return self.sql(
2775
2737
  exp.cast(
2776
- self.func(function_name, expression.this, formatted_time),
2738
+ self.func(function_name, value, formatted_time),
2777
2739
  exp.DataType(this=exp.DType.DATE),
2778
2740
  )
2779
2741
  )
2780
2742
 
2781
- def parsedatetime_sql(self, expression: exp.ParseDatetime) -> str:
2782
- formatted_time = self.format_time(expression)
2743
+ def _strptime_default_year(
2744
+ self, expression: exp.StrToTime | exp.StrToDate | exp.ParseDatetime
2745
+ ) -> tuple[exp.ExpOrStr, exp.ExpOrStr | None]:
2746
+ value: exp.ExpOrStr = expression.this
2747
+ formatted_time: exp.ExpOrStr | None = self.format_time(expression)
2783
2748
 
2784
- default_year = expression.args.get("default_year")
2785
- if default_year:
2786
- year_str = exp.Literal.string(f"{default_year.name} ")
2787
- fmt_prefix = exp.Literal.string("%Y ")
2788
- value = exp.DPipe(this=year_str, expression=expression.this)
2789
- fmt = exp.DPipe(this=fmt_prefix, expression=formatted_time)
2790
- return self.func("STRPTIME", value, fmt)
2749
+ if default_year := expression.args.get("default_year"):
2750
+ value = exp.DPipe(this=exp.Literal.string(f"{default_year.name} "), expression=value)
2751
+ formatted_time = exp.DPipe(this=exp.Literal.string("%Y "), expression=formatted_time)
2791
2752
 
2792
- return self.func("STRPTIME", expression.this, formatted_time)
2753
+ return value, formatted_time
2754
+
2755
+ def parsedatetime_sql(self, expression: exp.ParseDatetime) -> str:
2756
+ value, formatted_time = self._strptime_default_year(expression)
2757
+ return self.func("STRPTIME", value, formatted_time)
2793
2758
 
2794
2759
  def parsetime_sql(self, expression: exp.ParseTime) -> str:
2795
2760
  formatted_time = self.format_time(expression)
@@ -3152,6 +3117,11 @@ class DuckDBGenerator(generator.Generator):
3152
3117
 
3153
3118
  def countif_sql(self, expression: exp.CountIf) -> str:
3154
3119
  if self.dialect.version >= (1, 2):
3120
+ this = expression.this
3121
+ if expression.args.get("zero_on_all_null") and not isinstance(this, exp.Distinct):
3122
+ # DuckDB >= 1.2's COUNT_IF returns NULL when the condition is NULL on all rows,
3123
+ # so we wrap the condition in IS TRUE to preserve count-like semantics
3124
+ expression = exp.CountIf(this=exp.paren(this).is_(exp.true()))
3155
3125
  return self.function_fallback_sql(expression)
3156
3126
 
3157
3127
  # https://github.com/tobymao/sqlglot/pull/4749
@@ -4383,7 +4353,7 @@ class DuckDBGenerator(generator.Generator):
4383
4353
  unit = expression.args.get("unit")
4384
4354
  date = expression.this
4385
4355
 
4386
- week_start = _week_unit_to_dow(unit)
4356
+ week_start = week_unit_to_dow(unit)
4387
4357
  unit = unit_to_str(expression)
4388
4358
 
4389
4359
  if week_start: