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.
- {sqlglotc-30.11.0/sqlglotc.egg-info → sqlglotc-30.13.0}/PKG-INFO +3 -3
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/pyproject.toml +2 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/setup.py +7 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/aggregate.py +5 -4
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/array.py +4 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/constraints.py +5 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/core.py +23 -10
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/ddl.py +9 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/json.py +4 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/math.py +4 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/properties.py +5 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/query.py +2 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/string.py +11 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/temporal.py +19 -4
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generator.py +91 -28
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/bigquery.py +30 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/clickhouse.py +4 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/databricks.py +14 -0
- sqlglotc-30.13.0/sqlglot/generators/dax.py +7 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/dremio.py +8 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/drill.py +17 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/duckdb.py +174 -55
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/exasol.py +1 -3
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/hive.py +78 -27
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/mysql.py +4 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/oracle.py +1 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/postgres.py +9 -3
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/presto.py +66 -5
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/snowflake.py +72 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/spark.py +0 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/spark2.py +23 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/sqlite.py +6 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/starrocks.py +15 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/trino.py +1 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/tsql.py +5 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/annotate_types.py +22 -8
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/canonicalize_internal_names.py +19 -6
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify_columns.py +105 -25
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify_tables.py +7 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/resolver.py +27 -10
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/scope.py +4 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/simplify.py +59 -27
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parser.py +177 -80
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/bigquery.py +40 -21
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/clickhouse.py +15 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/databricks.py +16 -0
- sqlglotc-30.13.0/sqlglot/parsers/dax.py +48 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/drill.py +8 -0
- sqlglotc-30.13.0/sqlglot/parsers/druid.py +10 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/duckdb.py +4 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/exasol.py +4 -3
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/hive.py +2 -18
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/mysql.py +3 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/oracle.py +5 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/postgres.py +1 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/presto.py +6 -2
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/snowflake.py +80 -7
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/spark2.py +1 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/starrocks.py +4 -1
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/tsql.py +10 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/time.py +23 -23
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/tokenizer_core.py +14 -3
- {sqlglotc-30.11.0 → sqlglotc-30.13.0/sqlglotc.egg-info}/PKG-INFO +3 -3
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/SOURCES.txt +4 -0
- sqlglotc-30.13.0/sqlglotc.egg-info/requires.txt +6 -0
- sqlglotc-30.13.0/sqlglotc.egg-info/scm_file_list.json +7 -0
- sqlglotc-30.13.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.11.0/sqlglot/parsers/druid.py +0 -7
- sqlglotc-30.11.0/sqlglotc.egg-info/requires.txt +0 -6
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/MANIFEST.in +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/setup.cfg +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/builders.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/datatypes.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/python.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/singlestore.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/generators/teradata.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/lineage.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/dremio.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/prql.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/spark.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/sqlite.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/teradata.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/parsers/trino.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/schema.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.11.0 → sqlglotc-30.13.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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":
|
|
259
|
+
arg_types = {"this": True, "expression": False}
|
|
259
260
|
|
|
260
261
|
|
|
261
262
|
class RegrIntercept(Expression, AggFunc):
|
|
262
|
-
arg_types = {"this": True, "expression":
|
|
263
|
+
arg_types = {"this": True, "expression": False}
|
|
263
264
|
|
|
264
265
|
|
|
265
266
|
class RegrR2(Expression, AggFunc):
|
|
266
|
-
arg_types = {"this": True, "expression":
|
|
267
|
+
arg_types = {"this": True, "expression": False}
|
|
267
268
|
|
|
268
269
|
|
|
269
270
|
class RegrSlope(Expression, AggFunc):
|
|
270
|
-
arg_types = {"this": True, "expression":
|
|
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
|
-
|
|
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
|
|
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 = {
|
|
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
|
|
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"]
|
|
@@ -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
|
|
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 = {
|
|
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
|
-
|
|
1011
|
-
f"/*{self.sanitize_comment(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
|
|
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} {
|
|
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
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"),
|