sqlglotc 30.10.0__tar.gz → 30.12.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.10.0/sqlglotc.egg-info → sqlglotc-30.12.0}/PKG-INFO +4 -2
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/pyproject.toml +3 -3
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/setup.py +33 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/array.py +4 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/builders.py +6 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/core.py +31 -10
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/ddl.py +8 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/json.py +4 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/properties.py +5 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/query.py +2 -2
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/temporal.py +5 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generator.py +66 -15
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/bigquery.py +3 -3
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/clickhouse.py +4 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/databricks.py +11 -12
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/duckdb.py +155 -6
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/exasol.py +1 -3
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/mysql.py +4 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/postgres.py +1 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/presto.py +2 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/snowflake.py +15 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/starrocks.py +15 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/tsql.py +9 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/lineage.py +124 -20
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/annotate_types.py +4 -4
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/normalize_identifiers.py +4 -3
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/qualify_columns.py +44 -14
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/qualify_tables.py +7 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/resolver.py +10 -4
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/scope.py +32 -8
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/simplify.py +76 -34
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parser.py +63 -29
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/bigquery.py +12 -7
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/clickhouse.py +5 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/databricks.py +1 -0
- sqlglotc-30.12.0/sqlglot/parsers/druid.py +10 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/exasol.py +3 -2
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/postgres.py +1 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/snowflake.py +75 -3
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/starrocks.py +4 -1
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/tokenizer_core.py +2 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0/sqlglotc.egg-info}/PKG-INFO +4 -2
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglotc.egg-info/SOURCES.txt +2 -0
- sqlglotc-30.12.0/sqlglotc.egg-info/requires.txt +6 -0
- sqlglotc-30.12.0/sqlglotc.egg-info/scm_file_list.json +7 -0
- sqlglotc-30.12.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.10.0/sqlglot/parsers/druid.py +0 -7
- sqlglotc-30.10.0/sqlglotc.egg-info/requires.txt +0 -5
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/MANIFEST.in +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/setup.cfg +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/aggregate.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/constraints.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/datatypes.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/expressions/string.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/dremio.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/hive.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/oracle.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/python.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/singlestore.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/spark.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/spark2.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/sqlite.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/teradata.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/generators/trino.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/canonicalize_internal_names.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/dremio.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/drill.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/duckdb.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/hive.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/mysql.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/oracle.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/presto.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/prql.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/spark.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/spark2.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/sqlite.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/teradata.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/trino.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/parsers/tsql.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/schema.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.10.0 → sqlglotc-30.12.0}/sqlglotc.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglotc
|
|
3
|
-
Version: 30.
|
|
3
|
+
Version: 30.12.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
11
|
Provides-Extra: dev
|
|
11
12
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
12
13
|
Requires-Dist: setuptools_scm; extra == "dev"
|
|
13
|
-
Requires-Dist: sqlglot-mypy>=2.1.0.
|
|
14
|
+
Requires-Dist: sqlglot-mypy>=2.1.0.post3; extra == "dev"
|
|
15
|
+
Dynamic: requires-dist
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sqlglotc"
|
|
3
|
-
dynamic = ["version"]
|
|
3
|
+
dynamic = ["version", "dependencies"]
|
|
4
4
|
description = "mypyc-compiled extensions for sqlglot"
|
|
5
5
|
authors = [{ name = "Toby Mao", email = "toby.mao@gmail.com" }]
|
|
6
6
|
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.post3"]
|
|
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.post3",
|
|
21
21
|
"types-python-dateutil",
|
|
22
22
|
"sqlglot",
|
|
23
23
|
]
|
|
@@ -128,9 +128,42 @@ class sdist(_sdist):
|
|
|
128
128
|
shutil.rmtree(local_sqlglot, ignore_errors=True)
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
def _sqlglot_requirement():
|
|
132
|
+
"""Pin sqlglot to the matching version so resolvers keep the two packages in lockstep."""
|
|
133
|
+
try:
|
|
134
|
+
pkg_info = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PKG-INFO")
|
|
135
|
+
if os.path.isfile(pkg_info):
|
|
136
|
+
# Building from an sdist: no git metadata; the released version is frozen in PKG-INFO.
|
|
137
|
+
with open(pkg_info, encoding="utf-8") as fd:
|
|
138
|
+
version = next(
|
|
139
|
+
(line.split(":", 1)[1].strip() for line in fd if line.startswith("Version:")),
|
|
140
|
+
"",
|
|
141
|
+
)
|
|
142
|
+
else:
|
|
143
|
+
from setuptools_scm import get_version
|
|
144
|
+
|
|
145
|
+
version = get_version(
|
|
146
|
+
root="..",
|
|
147
|
+
relative_to=__file__,
|
|
148
|
+
local_scheme="no-local-version",
|
|
149
|
+
fallback_version="0.0.0",
|
|
150
|
+
)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
print(f"sqlglotc: failed to determine the sqlglot version to pin: {e}")
|
|
153
|
+
version = ""
|
|
154
|
+
|
|
155
|
+
# Dev and fallback (no git metadata) builds have no matching sqlglot release on PyPI;
|
|
156
|
+
# depend on sqlglot unpinned since the extensions can't be imported without it.
|
|
157
|
+
if version in ("", "0.0.0") or ".dev" in version:
|
|
158
|
+
return ["sqlglot"]
|
|
159
|
+
|
|
160
|
+
return [f"sqlglot=={version}"]
|
|
161
|
+
|
|
162
|
+
|
|
131
163
|
setup(
|
|
132
164
|
name="sqlglotc",
|
|
133
165
|
packages=[],
|
|
166
|
+
install_requires=_sqlglot_requirement(),
|
|
134
167
|
ext_modules=mypycify(
|
|
135
168
|
_source_paths(), opt_level=os.environ.get("MYPYC_OPT", "2"), separate=True, verbose=True
|
|
136
169
|
),
|
|
@@ -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
|
|
|
@@ -319,6 +319,11 @@ def parse_identifier(name: str | Identifier, dialect: DialectType = None) -> Ide
|
|
|
319
319
|
Returns:
|
|
320
320
|
The identifier ast node.
|
|
321
321
|
"""
|
|
322
|
+
if isinstance(name, str) and SAFE_IDENTIFIER_RE.match(name):
|
|
323
|
+
# Simple names parse to a single unquoted identifier in all dialects, so we can
|
|
324
|
+
# avoid the tokenizer/parser round-trip for them.
|
|
325
|
+
return Identifier(this=name, quoted=False)
|
|
326
|
+
|
|
322
327
|
try:
|
|
323
328
|
expression = maybe_parse(name, dialect=dialect, into=Identifier)
|
|
324
329
|
except (ParseError, TokenError):
|
|
@@ -822,7 +827,7 @@ def replace_tables(
|
|
|
822
827
|
mapping = {normalize_table_name(k, dialect=dialect): v for k, v in mapping.items()}
|
|
823
828
|
|
|
824
829
|
def _replace_tables(node: Expr) -> Expr:
|
|
825
|
-
if isinstance(node, Table) and node.
|
|
830
|
+
if isinstance(node, Table) and node.meta_get("replace") is not False:
|
|
826
831
|
original = normalize_table_name(node, dialect=dialect)
|
|
827
832
|
new_name = mapping.get(original)
|
|
828
833
|
|
|
@@ -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
|
|
|
@@ -240,6 +240,9 @@ class Expr:
|
|
|
240
240
|
def meta(self) -> dict[str, t.Any]:
|
|
241
241
|
raise NotImplementedError
|
|
242
242
|
|
|
243
|
+
def meta_get(self, key: str, default: t.Any = None) -> t.Any:
|
|
244
|
+
raise NotImplementedError
|
|
245
|
+
|
|
243
246
|
def __deepcopy__(self, memo: t.Any) -> Expr:
|
|
244
247
|
raise NotImplementedError
|
|
245
248
|
|
|
@@ -990,6 +993,11 @@ class Expression(Expr):
|
|
|
990
993
|
self._meta = {}
|
|
991
994
|
return self._meta
|
|
992
995
|
|
|
996
|
+
def meta_get(self, key: str, default: t.Any = None) -> t.Any:
|
|
997
|
+
"""Reads a meta value without allocating the meta dict (unlike the `meta` property)."""
|
|
998
|
+
meta = self._meta
|
|
999
|
+
return meta.get(key, default) if meta is not None else default
|
|
1000
|
+
|
|
993
1001
|
def __deepcopy__(self, memo: t.Any) -> Expr:
|
|
994
1002
|
root = self.__class__()
|
|
995
1003
|
stack: list[tuple[Expr, Expr]] = [(self, root)]
|
|
@@ -1781,7 +1789,12 @@ class JoinHint(Expression):
|
|
|
1781
1789
|
|
|
1782
1790
|
|
|
1783
1791
|
class Identifier(Expression):
|
|
1784
|
-
arg_types = {
|
|
1792
|
+
arg_types = {
|
|
1793
|
+
"this": True,
|
|
1794
|
+
"quoted": False,
|
|
1795
|
+
"global_": False,
|
|
1796
|
+
"temporary": False,
|
|
1797
|
+
}
|
|
1785
1798
|
is_primitive = True
|
|
1786
1799
|
_hash_raw_args = True
|
|
1787
1800
|
|
|
@@ -1794,12 +1807,19 @@ class Identifier(Expression):
|
|
|
1794
1807
|
return self.name
|
|
1795
1808
|
|
|
1796
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
|
+
|
|
1797
1817
|
class Opclass(Expression):
|
|
1798
1818
|
arg_types = {"this": True, "expression": True}
|
|
1799
1819
|
|
|
1800
1820
|
|
|
1801
1821
|
class Star(Expression):
|
|
1802
|
-
arg_types = {"except_": False, "replace": False, "rename": False}
|
|
1822
|
+
arg_types = {"except_": False, "replace": False, "rename": False, "ilike": False}
|
|
1803
1823
|
|
|
1804
1824
|
@property
|
|
1805
1825
|
def name(self) -> str:
|
|
@@ -2407,7 +2427,8 @@ def convert(value: t.Any, copy: bool = False) -> Expr:
|
|
|
2407
2427
|
|
|
2408
2428
|
return _Array(expressions=[convert(v, copy=copy) for v in value])
|
|
2409
2429
|
if isinstance(value, dict):
|
|
2410
|
-
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
|
|
2411
2432
|
|
|
2412
2433
|
return _Map(
|
|
2413
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
|
|
|
@@ -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):
|
|
@@ -437,8 +437,12 @@ 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
|
+
|
|
440
444
|
class ParseDatetime(Expression, Func):
|
|
441
|
-
arg_types = {"this": True, "format": False, "zone": False}
|
|
445
|
+
arg_types = {"this": True, "format": False, "zone": False, "default_year": False}
|
|
442
446
|
|
|
443
447
|
|
|
444
448
|
class ParseTime(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),
|
|
@@ -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 = (
|
|
@@ -1945,6 +1957,20 @@ class Generator:
|
|
|
1945
1957
|
params = self.sql(expression, "params")
|
|
1946
1958
|
return f"{unique}{primary}{amp}{index}{name}{table}{params}"
|
|
1947
1959
|
|
|
1960
|
+
def dynamicidentifier_sql(self, expression: exp.DynamicIdentifier) -> str:
|
|
1961
|
+
this = expression.this
|
|
1962
|
+
if this and this.is_string:
|
|
1963
|
+
resolved = maybe_parse(this.name).sql(self.dialect)
|
|
1964
|
+
if "expressions" in expression.args:
|
|
1965
|
+
# `IDENTIFIER(...)` invoked as a function, e.g. `IDENTIFIER('my_func')(1, 2)`
|
|
1966
|
+
# We can't safely emit the call to other dialects since name/arg semantics may differ
|
|
1967
|
+
self.unsupported(
|
|
1968
|
+
"Transpiling dynamically-invoked IDENTIFIER() functions is unsupported"
|
|
1969
|
+
)
|
|
1970
|
+
return resolved
|
|
1971
|
+
self.unsupported("IDENTIFIER() with non-literal arguments is not supported")
|
|
1972
|
+
return self.func("IDENTIFIER", this)
|
|
1973
|
+
|
|
1948
1974
|
def identifier_sql(self, expression: exp.Identifier) -> str:
|
|
1949
1975
|
text = expression.name
|
|
1950
1976
|
lower = text.lower()
|
|
@@ -2393,10 +2419,12 @@ class Generator:
|
|
|
2393
2419
|
laterals = self.expressions(expression, key="laterals", sep="")
|
|
2394
2420
|
|
|
2395
2421
|
file_format = self.sql(expression, "format")
|
|
2422
|
+
pattern = self.sql(expression, "pattern")
|
|
2396
2423
|
if file_format:
|
|
2397
|
-
pattern = self.sql(expression, "pattern")
|
|
2398
2424
|
pattern = f", PATTERN => {pattern}" if pattern else ""
|
|
2399
2425
|
file_format = f" (FILE_FORMAT => {file_format}{pattern})"
|
|
2426
|
+
elif pattern:
|
|
2427
|
+
file_format = f" (PATTERN => {pattern})"
|
|
2400
2428
|
|
|
2401
2429
|
ordinality = expression.args.get("ordinality") or ""
|
|
2402
2430
|
if ordinality:
|
|
@@ -3371,7 +3399,9 @@ class Generator:
|
|
|
3371
3399
|
replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
|
|
3372
3400
|
rename = self.expressions(expression, key="rename", flat=True)
|
|
3373
3401
|
rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
|
|
3374
|
-
|
|
3402
|
+
ilike = self.sql(expression, "ilike")
|
|
3403
|
+
ilike = f"{self.seg('ILIKE')} {ilike}" if ilike else ""
|
|
3404
|
+
return f"*{ilike}{except_}{replace}{rename}"
|
|
3375
3405
|
|
|
3376
3406
|
def parameter_sql(self, expression: exp.Parameter) -> str:
|
|
3377
3407
|
this = self.sql(expression, "this")
|
|
@@ -3719,9 +3749,6 @@ class Generator:
|
|
|
3719
3749
|
def jsonpath_sql(self, expression: exp.JSONPath) -> str:
|
|
3720
3750
|
path = self.expressions(expression, sep="", flat=True).lstrip(".")
|
|
3721
3751
|
|
|
3722
|
-
if expression.args.get("escape"):
|
|
3723
|
-
path = self.escape_str(path)
|
|
3724
|
-
|
|
3725
3752
|
if self.QUOTE_JSON_PATH:
|
|
3726
3753
|
path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
|
|
3727
3754
|
|
|
@@ -3975,6 +4002,12 @@ class Generator:
|
|
|
3975
4002
|
zone = self.sql(expression, "zone")
|
|
3976
4003
|
return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
|
|
3977
4004
|
|
|
4005
|
+
def fromiso8601date_sql(self, expression: exp.FromISO8601Date) -> str:
|
|
4006
|
+
return self.sql(exp.cast(expression.this, exp.DType.DATE))
|
|
4007
|
+
|
|
4008
|
+
def fromiso8601timestamp_sql(self, expression: exp.FromISO8601Timestamp) -> str:
|
|
4009
|
+
return self.sql(exp.cast(expression.this, exp.DType.TIMESTAMPTZ))
|
|
4010
|
+
|
|
3978
4011
|
def add_sql(self, expression: exp.Add) -> str:
|
|
3979
4012
|
return self.binary(expression, "+")
|
|
3980
4013
|
|
|
@@ -4054,6 +4087,14 @@ class Generator:
|
|
|
4054
4087
|
def strtotime_sql(self, expression: exp.StrToTime) -> str:
|
|
4055
4088
|
return self.func("STR_TO_TIME", expression.this, expression.args.get("format"))
|
|
4056
4089
|
|
|
4090
|
+
def parsedatetime_sql(self, expression: exp.ParseDatetime) -> str:
|
|
4091
|
+
return self.func(
|
|
4092
|
+
"PARSE_DATETIME",
|
|
4093
|
+
expression.this,
|
|
4094
|
+
expression.args.get("format"),
|
|
4095
|
+
expression.args.get("zone"),
|
|
4096
|
+
)
|
|
4097
|
+
|
|
4057
4098
|
def currentdate_sql(self, expression: exp.CurrentDate) -> str:
|
|
4058
4099
|
zone = self.sql(expression, "this")
|
|
4059
4100
|
return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
|
|
@@ -4554,7 +4595,7 @@ class Generator:
|
|
|
4554
4595
|
args.append(arg_value)
|
|
4555
4596
|
|
|
4556
4597
|
if self.dialect.PRESERVE_ORIGINAL_NAMES:
|
|
4557
|
-
name =
|
|
4598
|
+
name = expression.meta_get("name") or expression.sql_name()
|
|
4558
4599
|
else:
|
|
4559
4600
|
name = expression.sql_name()
|
|
4560
4601
|
|
|
@@ -5228,10 +5269,20 @@ class Generator:
|
|
|
5228
5269
|
this = self.json_path_part(this)
|
|
5229
5270
|
return f".{this}" if this else ""
|
|
5230
5271
|
|
|
5231
|
-
|
|
5272
|
+
quoted = expression.args.get("quoted")
|
|
5273
|
+
if not (
|
|
5274
|
+
quoted and self.JSON_PATH_KEY_QUOTED_FORCES_BRACKETS
|
|
5275
|
+
) and self.SAFE_JSON_PATH_KEY_RE.match(this):
|
|
5232
5276
|
return f".{this}"
|
|
5233
5277
|
|
|
5234
5278
|
this = self.json_path_part(this)
|
|
5279
|
+
|
|
5280
|
+
if quoted and self.QUOTE_JSON_PATH:
|
|
5281
|
+
# The whole path is rendered as a single quoted string literal, so the bracketed key
|
|
5282
|
+
# (which may itself contain backslash-escaped quotes, e.g. ["x \"y\"z"]) must be
|
|
5283
|
+
# escaped again for the outer string literal (-> ["x \\"y\\"z"]).
|
|
5284
|
+
this = self.escape_str(this)
|
|
5285
|
+
|
|
5235
5286
|
return (
|
|
5236
5287
|
f"[{this}]"
|
|
5237
5288
|
if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
|
|
@@ -5258,7 +5309,7 @@ class Generator:
|
|
|
5258
5309
|
)
|
|
5259
5310
|
return self.sql(this)
|
|
5260
5311
|
|
|
5261
|
-
if self.IGNORE_NULLS_IN_FUNC and not expression.
|
|
5312
|
+
if self.IGNORE_NULLS_IN_FUNC and not expression.meta_get("inline"):
|
|
5262
5313
|
if self.IGNORE_NULLS_BEFORE_ORDER:
|
|
5263
5314
|
# The first modifier here will be the one closest to the AggFunc's arg
|
|
5264
5315
|
mods = sorted(
|
|
@@ -201,7 +201,7 @@ def _levenshtein_sql(self: BigQueryGenerator, expression: exp.Levenshtein) -> st
|
|
|
201
201
|
|
|
202
202
|
|
|
203
203
|
def _json_extract_sql(self: BigQueryGenerator, expression: JSON_EXTRACT_TYPE) -> str:
|
|
204
|
-
name =
|
|
204
|
+
name = expression.meta_get("name") or expression.sql_name()
|
|
205
205
|
upper = name.upper()
|
|
206
206
|
|
|
207
207
|
dquote_escaping = upper in DQUOTES_ESCAPING_JSON_FUNCTIONS
|
|
@@ -565,7 +565,7 @@ class BigQueryGenerator(generator.Generator):
|
|
|
565
565
|
)
|
|
566
566
|
|
|
567
567
|
def column_parts(self, expression: exp.Column) -> str:
|
|
568
|
-
if expression.
|
|
568
|
+
if expression.meta_get("quoted_column"):
|
|
569
569
|
# If a column reference is of the form `dataset.table`.name, we need
|
|
570
570
|
# to preserve the quoted table path, otherwise the reference breaks
|
|
571
571
|
table_parts = ".".join(p.name for p in expression.parts[:-1])
|
|
@@ -583,7 +583,7 @@ class BigQueryGenerator(generator.Generator):
|
|
|
583
583
|
#
|
|
584
584
|
# - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join
|
|
585
585
|
# - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest
|
|
586
|
-
if expression.
|
|
586
|
+
if expression.meta_get("quoted_table"):
|
|
587
587
|
table_parts = ".".join(p.name for p in expression.parts)
|
|
588
588
|
return self.sql(exp.Identifier(this=table_parts, quoted=True))
|
|
589
589
|
|
|
@@ -280,6 +280,7 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
280
280
|
exp.ArrayConcat: rename_func("arrayConcat"),
|
|
281
281
|
exp.ArrayContains: rename_func("has"),
|
|
282
282
|
exp.ArrayFilter: lambda self, e: self.func("arrayFilter", e.expression, e.this),
|
|
283
|
+
exp.Transform: lambda self, e: self.func("arrayMap", e.expression, e.this),
|
|
283
284
|
exp.ArrayRemove: remove_from_array_using_filter,
|
|
284
285
|
exp.ArrayReverse: rename_func("arrayReverse"),
|
|
285
286
|
exp.ArraySlice: rename_func("arraySlice"),
|
|
@@ -377,7 +378,9 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
377
378
|
exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")(
|
|
378
379
|
rename_func("editDistance")
|
|
379
380
|
),
|
|
380
|
-
exp.ParseDatetime:
|
|
381
|
+
exp.ParseDatetime: lambda self, e: self.func(
|
|
382
|
+
"parseDateTime", e.this, e.args.get("format"), e.args.get("zone")
|
|
383
|
+
),
|
|
381
384
|
}
|
|
382
385
|
|
|
383
386
|
PROPERTIES_LOCATION = {
|
|
@@ -10,19 +10,12 @@ from sqlglot.dialects.dialect import (
|
|
|
10
10
|
from sqlglot.generators.spark import SparkGenerator
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def _jsonextract_sql(
|
|
14
|
-
self: DatabricksGenerator, expression: exp.JSONExtract | exp.JSONExtractScalar
|
|
15
|
-
) -> str:
|
|
16
|
-
this = self.sql(expression, "this")
|
|
17
|
-
expr = self.sql(expression, "expression")
|
|
18
|
-
return f"{this}:{expr}"
|
|
19
|
-
|
|
20
|
-
|
|
21
13
|
class DatabricksGenerator(SparkGenerator):
|
|
22
14
|
TABLESAMPLE_SEED_KEYWORD = "REPEATABLE"
|
|
23
15
|
COPY_PARAMS_ARE_WRAPPED = False
|
|
24
16
|
COPY_PARAMS_EQ_REQUIRED = True
|
|
25
17
|
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
|
|
18
|
+
JSON_PATH_KEY_QUOTED_FORCES_BRACKETS = True
|
|
26
19
|
SAFE_JSON_PATH_KEY_RE = exp.SAFE_IDENTIFIER_RE
|
|
27
20
|
QUOTE_JSON_PATH = False
|
|
28
21
|
PARSE_JSON_NAME: str | None = "PARSE_JSON"
|
|
@@ -52,9 +45,10 @@ class DatabricksGenerator(SparkGenerator):
|
|
|
52
45
|
transforms.any_to_exists,
|
|
53
46
|
]
|
|
54
47
|
),
|
|
55
|
-
exp.JSONExtract:
|
|
56
|
-
exp.
|
|
57
|
-
|
|
48
|
+
exp.JSONExtract: lambda self, e: f"{self.sql(e, 'this')}:{self.sql(e, 'expression')}",
|
|
49
|
+
exp.JSONPathRoot: lambda self, e: (
|
|
50
|
+
"$" if isinstance(e.parent and e.parent.parent, exp.JSONExtractScalar) else ""
|
|
51
|
+
),
|
|
58
52
|
exp.ToChar: lambda self, e: (
|
|
59
53
|
self.cast_sql(exp.Cast(this=e.this, to=exp.DataType(this="STRING")))
|
|
60
54
|
if e.args.get("is_numeric")
|
|
@@ -98,7 +92,12 @@ class DatabricksGenerator(SparkGenerator):
|
|
|
98
92
|
|
|
99
93
|
def jsonpath_sql(self, expression: exp.JSONPath) -> str:
|
|
100
94
|
expression.set("escape", None)
|
|
101
|
-
|
|
95
|
+
path = super().jsonpath_sql(expression)
|
|
96
|
+
|
|
97
|
+
if isinstance(expression.parent, exp.JSONExtractScalar):
|
|
98
|
+
return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
|
|
99
|
+
|
|
100
|
+
return path
|
|
102
101
|
|
|
103
102
|
def uniform_sql(self, expression: exp.Uniform) -> str:
|
|
104
103
|
gen = expression.args.get("gen")
|