sqlglotc 30.24.0__tar.gz → 30.26.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.24.0/sqlglotc.egg-info → sqlglotc-30.26.0}/PKG-INFO +3 -3
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/pyproject.toml +3 -2
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/array.py +4 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/builders.py +1 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/constraints.py +2 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/core.py +11 -8
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/datatypes.py +3 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/ddl.py +8 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/properties.py +4 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/query.py +8 -3
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generator.py +133 -24
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/bigquery.py +1 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/clickhouse.py +7 -7
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/databricks.py +1 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/duckdb.py +111 -14
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/exasol.py +3 -6
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/hive.py +4 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/oracle.py +0 -3
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/postgres.py +67 -11
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/presto.py +7 -2
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/python.py +5 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/snowflake.py +3 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/sqlite.py +6 -2
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/tsql.py +5 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/annotate_types.py +239 -176
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/canonicalize_internal_names.py +11 -10
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/qualify_columns.py +94 -158
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/resolver.py +60 -9
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/scope.py +23 -4
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/simplify.py +5 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parser.py +102 -27
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/clickhouse.py +6 -9
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/duckdb.py +95 -3
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/mysql.py +8 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/oracle.py +18 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/postgres.py +56 -5
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/snowflake.py +37 -16
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/spark.py +1 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/sqlite.py +32 -1
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/tokenizer_core.py +2 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0/sqlglotc.egg-info}/PKG-INFO +3 -3
- sqlglotc-30.26.0/sqlglotc.egg-info/requires.txt +6 -0
- sqlglotc-30.26.0/sqlglotc.egg-info/scm_version.json +8 -0
- sqlglotc-30.24.0/sqlglotc.egg-info/requires.txt +0 -6
- sqlglotc-30.24.0/sqlglotc.egg-info/scm_version.json +0 -8
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/MANIFEST.in +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/setup.cfg +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/setup.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/anonymize.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/errors.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/executor/table.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/aggregate.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/dml.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/functions.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/json.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/math.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/string.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/expressions/temporal.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/athena.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/dax.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/doris.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/dremio.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/drill.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/druid.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/dune.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/fabric.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/materialize.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/mysql.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/prql.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/redshift.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/risingwave.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/singlestore.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/solr.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/spark.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/spark2.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/starrocks.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/tableau.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/teradata.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/generators/trino.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/helper.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/lineage.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/isolate_table_selects.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/normalize_identifiers.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/qualify.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/optimizer/qualify_tables.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/athena.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/base.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/bigquery.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/databricks.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/dax.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/doris.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/dremio.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/drill.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/druid.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/dune.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/exasol.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/fabric.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/hive.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/materialize.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/presto.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/prql.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/redshift.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/risingwave.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/singlestore.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/solr.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/spark2.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/starrocks.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/tableau.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/teradata.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/trino.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/parsers/tsql.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/serde.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/time.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglot/trie.py +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/SOURCES.txt +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/dependency_links.txt +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.0}/sqlglotc.egg-info/scm_file_list.json +0 -0
- {sqlglotc-30.24.0 → sqlglotc-30.26.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.26.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.26.0
|
|
11
11
|
Provides-Extra: dev
|
|
12
12
|
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
13
|
-
Requires-Dist: setuptools_scm; extra == "dev"
|
|
13
|
+
Requires-Dist: setuptools_scm>=10.2; extra == "dev"
|
|
14
14
|
Requires-Dist: mypy>=2.4.0; 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", "mypy >= 2.4.0"]
|
|
10
|
+
dev = ["setuptools >= 61.0", "setuptools_scm >= 10.2", "mypy >= 2.4.0"]
|
|
11
11
|
|
|
12
12
|
[project.urls]
|
|
13
13
|
Homepage = "https://sqlglot.com/"
|
|
@@ -16,7 +16,7 @@ Repository = "https://github.com/tobymao/sqlglot"
|
|
|
16
16
|
[build-system]
|
|
17
17
|
requires = [
|
|
18
18
|
"setuptools >= 61.0",
|
|
19
|
-
"setuptools_scm",
|
|
19
|
+
"setuptools_scm >= 10.2",
|
|
20
20
|
"mypy >= 2.4.0",
|
|
21
21
|
"types-python-dateutil",
|
|
22
22
|
"sqlglot",
|
|
@@ -28,6 +28,7 @@ include-package-data = false
|
|
|
28
28
|
|
|
29
29
|
[tool.setuptools_scm]
|
|
30
30
|
root = ".."
|
|
31
|
+
write_to_source = true
|
|
31
32
|
fallback_version = "0.0.0"
|
|
32
33
|
local_scheme = "no-local-version"
|
|
33
34
|
|
|
@@ -254,6 +254,10 @@ class SplitToTable(Expression, Func, UDTF):
|
|
|
254
254
|
arg_types = {"this": True, "expression": True}
|
|
255
255
|
|
|
256
256
|
|
|
257
|
+
class StrtokSplitToTable(Expression, Func, UDTF):
|
|
258
|
+
arg_types = {"this": True, "expression": False}
|
|
259
|
+
|
|
260
|
+
|
|
257
261
|
class Stack(Expression, Func, UDTF):
|
|
258
262
|
arg_types = {"this": True, "expressions": True}
|
|
259
263
|
is_var_len_args = True
|
|
@@ -921,7 +921,7 @@ def replace_placeholders(expression: Expr, *args: object, **kwargs: t.Any) -> Ex
|
|
|
921
921
|
def _replace_placeholders(node: Expr, args: Iterator[object], **kwargs: object) -> Expr:
|
|
922
922
|
if isinstance(node, Placeholder):
|
|
923
923
|
if node.this:
|
|
924
|
-
new_name = kwargs.get(node.
|
|
924
|
+
new_name = kwargs.get(node.name)
|
|
925
925
|
if new_name is not None:
|
|
926
926
|
return _replace_placeholder(node, convert(new_name))
|
|
927
927
|
else:
|
|
@@ -161,7 +161,7 @@ class OnUpdateColumnConstraint(Expression, ColumnConstraintKind):
|
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
class PrimaryKeyColumnConstraint(Expression, ColumnConstraintKind):
|
|
164
|
-
arg_types = {"desc": False, "options": False}
|
|
164
|
+
arg_types = {"desc": False, "options": False, "include": False}
|
|
165
165
|
|
|
166
166
|
|
|
167
167
|
class TitleColumnConstraint(Expression, ColumnConstraintKind):
|
|
@@ -175,6 +175,7 @@ class UniqueColumnConstraint(Expression, ColumnConstraintKind):
|
|
|
175
175
|
"on_conflict": False,
|
|
176
176
|
"nulls": False,
|
|
177
177
|
"options": False,
|
|
178
|
+
"include": False,
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
|
|
@@ -1734,13 +1734,13 @@ class Column(Expression, Condition):
|
|
|
1734
1734
|
return self.name
|
|
1735
1735
|
|
|
1736
1736
|
@property
|
|
1737
|
-
def parts(self) -> list[
|
|
1737
|
+
def parts(self) -> list[Expr]:
|
|
1738
1738
|
"""Return the parts of a column in order catalog, db, table, name."""
|
|
1739
1739
|
return [
|
|
1740
1740
|
self.args[part] for part in ("catalog", "db", "table", "this") if self.args.get(part)
|
|
1741
1741
|
]
|
|
1742
1742
|
|
|
1743
|
-
def to_dot(self, include_dots: bool = True) ->
|
|
1743
|
+
def to_dot(self, include_dots: bool = True) -> Expr:
|
|
1744
1744
|
"""Converts the column into a dot expression."""
|
|
1745
1745
|
parts = self.parts
|
|
1746
1746
|
parent = self.parent
|
|
@@ -2108,6 +2108,10 @@ class Filter(Expression):
|
|
|
2108
2108
|
arg_types = {"this": True, "expression": True}
|
|
2109
2109
|
|
|
2110
2110
|
|
|
2111
|
+
class Per(Expression):
|
|
2112
|
+
arg_types = {"this": True, "expressions": True}
|
|
2113
|
+
|
|
2114
|
+
|
|
2111
2115
|
class Check(Expression):
|
|
2112
2116
|
pass
|
|
2113
2117
|
|
|
@@ -3071,7 +3075,7 @@ def alias_(
|
|
|
3071
3075
|
|
|
3072
3076
|
@t.overload
|
|
3073
3077
|
def column(
|
|
3074
|
-
col: str |
|
|
3078
|
+
col: str | Expr,
|
|
3075
3079
|
table: str | Identifier | None = None,
|
|
3076
3080
|
db: str | Identifier | None = None,
|
|
3077
3081
|
catalog: str | Identifier | None = None,
|
|
@@ -3085,7 +3089,7 @@ def column(
|
|
|
3085
3089
|
|
|
3086
3090
|
@t.overload
|
|
3087
3091
|
def column(
|
|
3088
|
-
col: str |
|
|
3092
|
+
col: str | Expr,
|
|
3089
3093
|
table: str | Identifier | None = None,
|
|
3090
3094
|
db: str | Identifier | None = None,
|
|
3091
3095
|
catalog: str | Identifier | None = None,
|
|
@@ -3111,19 +3115,18 @@ def column(
|
|
|
3111
3115
|
Build a Column.
|
|
3112
3116
|
|
|
3113
3117
|
Args:
|
|
3114
|
-
col: Column name.
|
|
3118
|
+
col: Column name or expression.
|
|
3115
3119
|
table: Table name.
|
|
3116
3120
|
db: Database name.
|
|
3117
3121
|
catalog: Catalog name.
|
|
3118
3122
|
fields: Additional fields using dots.
|
|
3119
3123
|
quoted: Whether to force quotes on the column's identifiers.
|
|
3120
|
-
copy: Whether to copy
|
|
3124
|
+
copy: Whether to copy expressions if passed in.
|
|
3121
3125
|
|
|
3122
3126
|
Returns:
|
|
3123
3127
|
The new Column instance.
|
|
3124
3128
|
"""
|
|
3125
|
-
if
|
|
3126
|
-
col = to_identifier(col, quoted=quoted, copy=copy)
|
|
3129
|
+
col = maybe_copy(col, copy) if isinstance(col, Expr) else to_identifier(col, quoted=quoted)
|
|
3127
3130
|
|
|
3128
3131
|
this: Column | Dot = Column(
|
|
3129
3132
|
this=col,
|
|
@@ -461,7 +461,9 @@ class IntervalSpan(DataType):
|
|
|
461
461
|
|
|
462
462
|
|
|
463
463
|
class Interval(_TimeUnit):
|
|
464
|
-
|
|
464
|
+
# fraction_in_string: a fractional amount was written with its unit in one string, e.g.
|
|
465
|
+
# INTERVAL '1.5 DAY', which differs from INTERVAL '1.5' DAY in e.g. DuckDB and Postgres
|
|
466
|
+
arg_types = {"this": False, "unit": False, "fraction_in_string": False}
|
|
465
467
|
|
|
466
468
|
|
|
467
469
|
DATA_TYPE = t.Union[str, Identifier, Dot, DataType, DType]
|
|
@@ -131,6 +131,14 @@ class Describe(Expression):
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
class ConnectTo(Expression):
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class Disconnect(Expression):
|
|
139
|
+
arg_types = {}
|
|
140
|
+
|
|
141
|
+
|
|
134
142
|
class Attach(Expression):
|
|
135
143
|
arg_types = {"this": True, "exists": False, "expressions": False}
|
|
136
144
|
|
|
@@ -663,7 +663,7 @@ class GroupingSets(Expression):
|
|
|
663
663
|
|
|
664
664
|
|
|
665
665
|
class Lambda(Expression):
|
|
666
|
-
arg_types = {"this": True, "expressions": True
|
|
666
|
+
arg_types = {"this": True, "expressions": True}
|
|
667
667
|
|
|
668
668
|
|
|
669
669
|
class Limit(Expression):
|
|
@@ -699,8 +699,9 @@ class Join(Expression):
|
|
|
699
699
|
"method": False,
|
|
700
700
|
"global_": False,
|
|
701
701
|
"hint": False,
|
|
702
|
-
"match_condition": False,
|
|
703
|
-
"
|
|
702
|
+
"match_condition": False,
|
|
703
|
+
"nearest": False,
|
|
704
|
+
"directed": False,
|
|
704
705
|
"expressions": False,
|
|
705
706
|
"pivots": False,
|
|
706
707
|
}
|
|
@@ -817,6 +818,10 @@ class Join(Expression):
|
|
|
817
818
|
return join
|
|
818
819
|
|
|
819
820
|
|
|
821
|
+
class Nearest(Expression):
|
|
822
|
+
arg_types = {"this": True, "count": True, "approximate": False, "similarity": False}
|
|
823
|
+
|
|
824
|
+
|
|
820
825
|
class Lateral(Expression, UDTF):
|
|
821
826
|
arg_types = {
|
|
822
827
|
"this": True,
|
|
@@ -169,6 +169,7 @@ class Generator:
|
|
|
169
169
|
"CONVERT", e.this, e.args["dest"], e.args.get("source")
|
|
170
170
|
),
|
|
171
171
|
exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
|
|
172
|
+
exp.CopyTagsProperty: lambda *_: "COPY TAGS",
|
|
172
173
|
exp.CredentialsProperty: lambda self, e: (
|
|
173
174
|
f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})"
|
|
174
175
|
),
|
|
@@ -333,6 +334,9 @@ class Generator:
|
|
|
333
334
|
# Whether MERGE ... WHEN MATCHED/NOT MATCHED THEN UPDATE/INSERT ... WHERE is supported
|
|
334
335
|
SUPPORTS_MERGE_WHERE = False
|
|
335
336
|
|
|
337
|
+
# Whether MERGE uses ALL BY NAME instead of a star to update or insert all columns
|
|
338
|
+
MERGE_ALL_BY_NAME = False
|
|
339
|
+
|
|
336
340
|
# Whether the INTERVAL expression works only with values like '1 day'
|
|
337
341
|
SINGLE_STRING_INTERVAL = False
|
|
338
342
|
|
|
@@ -370,6 +374,9 @@ class Generator:
|
|
|
370
374
|
# Whether directed joins are supported
|
|
371
375
|
DIRECTED_JOINS = False
|
|
372
376
|
|
|
377
|
+
# Whether top-k distance/similarity joins are supported
|
|
378
|
+
SUPPORTS_NEAREST_JOIN = False
|
|
379
|
+
|
|
373
380
|
# Whether table hints should be generated
|
|
374
381
|
TABLE_HINTS = True
|
|
375
382
|
|
|
@@ -414,9 +421,12 @@ class Generator:
|
|
|
414
421
|
# SELECT * VALUES into SELECT UNION
|
|
415
422
|
VALUES_AS_TABLE = True
|
|
416
423
|
|
|
417
|
-
# Whether
|
|
424
|
+
# Whether COLUMN/COLUMNS keywords are included when adding columns with ALTER TABLE.
|
|
418
425
|
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
|
|
419
426
|
|
|
427
|
+
# Whether ALTER TABLE mutli-column additions are wrapped in parentheses.
|
|
428
|
+
ALTER_TABLE_ADD_COLUMNS_WRAPPED = True
|
|
429
|
+
|
|
420
430
|
# UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
|
|
421
431
|
UNNEST_WITH_ORDINALITY = True
|
|
422
432
|
|
|
@@ -722,6 +732,7 @@ class Generator:
|
|
|
722
732
|
exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
|
|
723
733
|
exp.ComputeProperty: exp.Properties.Location.POST_CREATE,
|
|
724
734
|
exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
|
|
735
|
+
exp.CopyTagsProperty: exp.Properties.Location.POST_SCHEMA,
|
|
725
736
|
exp.Cluster: exp.Properties.Location.POST_SCHEMA,
|
|
726
737
|
exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
|
|
727
738
|
exp.ClusterProperty: exp.Properties.Location.POST_SCHEMA,
|
|
@@ -1322,7 +1333,8 @@ class Generator:
|
|
|
1322
1333
|
return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
|
|
1323
1334
|
options = self.expressions(expression, key="options", flat=True, sep=" ")
|
|
1324
1335
|
options = f" {options}" if options else ""
|
|
1325
|
-
|
|
1336
|
+
include = self.sql(expression, "include")
|
|
1337
|
+
return f"PRIMARY KEY{include}{options}"
|
|
1326
1338
|
|
|
1327
1339
|
def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
|
|
1328
1340
|
this = self.sql(expression, "this")
|
|
@@ -1334,7 +1346,8 @@ class Generator:
|
|
|
1334
1346
|
nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
|
|
1335
1347
|
options = self.expressions(expression, key="options", flat=True, sep=" ")
|
|
1336
1348
|
options = f" {options}" if options else ""
|
|
1337
|
-
|
|
1349
|
+
include = self.sql(expression, "include")
|
|
1350
|
+
return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{include}{options}"
|
|
1338
1351
|
|
|
1339
1352
|
def inoutcolumnconstraint_sql(self, expression: exp.InOutColumnConstraint) -> str:
|
|
1340
1353
|
input_ = expression.args.get("input_")
|
|
@@ -1953,19 +1966,63 @@ class Generator:
|
|
|
1953
1966
|
return self.sql(select)
|
|
1954
1967
|
|
|
1955
1968
|
sqls: list[str] = []
|
|
1956
|
-
stack: list[str | exp.Expr] = [expression]
|
|
1969
|
+
stack: list[str | exp.Expr | int | None] = [expression]
|
|
1970
|
+
depth = 0
|
|
1971
|
+
open_parens = ""
|
|
1957
1972
|
|
|
1958
1973
|
while stack:
|
|
1959
1974
|
node = stack.pop()
|
|
1960
1975
|
|
|
1961
|
-
if isinstance(node,
|
|
1976
|
+
if isinstance(node, int):
|
|
1977
|
+
# Closes a plain grouping opened below, restoring its depth
|
|
1978
|
+
depth = node
|
|
1979
|
+
sqls[-1] += self.sep("") + self.indent(")", level=depth, pad=0)
|
|
1980
|
+
elif isinstance(node, exp.SetOperation):
|
|
1962
1981
|
stack.append(node.expression)
|
|
1963
1982
|
stack.append(
|
|
1964
1983
|
self.maybe_comment(
|
|
1965
1984
|
self.set_operation(node), comments=node.comments, separated=True
|
|
1966
1985
|
)
|
|
1967
1986
|
)
|
|
1968
|
-
|
|
1987
|
+
left = node.this
|
|
1988
|
+
if (
|
|
1989
|
+
not self.SET_OP_PARENTHESIZED_OPERANDS
|
|
1990
|
+
and not self.dialect.INTERSECT_BINDS_TIGHTER_THAN_UNION_AND_EXCEPT
|
|
1991
|
+
):
|
|
1992
|
+
# Left grouping is implicit, so skip the `SELECT * FROM (...)` wrapper:
|
|
1993
|
+
# it renames duplicate columns and deepens recursion. Inner CTEs and
|
|
1994
|
+
# modifiers still need the wrapper.
|
|
1995
|
+
wrappers: list[exp.Subquery] = []
|
|
1996
|
+
inner = left
|
|
1997
|
+
while isinstance(inner, exp.Subquery) and inner.is_wrapper:
|
|
1998
|
+
wrappers.append(inner)
|
|
1999
|
+
inner = inner.this
|
|
2000
|
+
|
|
2001
|
+
if (
|
|
2002
|
+
isinstance(inner, exp.SetOperation)
|
|
2003
|
+
and not inner.args.get("with_")
|
|
2004
|
+
and not any(inner.args.get(key) for key in exp.QUERY_MODIFIERS)
|
|
2005
|
+
):
|
|
2006
|
+
for wrapper in wrappers:
|
|
2007
|
+
inner.add_comments(wrapper.pop_comments())
|
|
2008
|
+
left = inner
|
|
2009
|
+
elif (
|
|
2010
|
+
self.SET_OP_PARENTHESIZED_OPERANDS
|
|
2011
|
+
and isinstance(left, exp.Subquery)
|
|
2012
|
+
and left.is_wrapper
|
|
2013
|
+
and not left.comments
|
|
2014
|
+
and isinstance(left.this, exp.SetOperation)
|
|
2015
|
+
and not left.this.args.get("with_")
|
|
2016
|
+
and not any(left.this.args.get(key) for key in exp.QUERY_MODIFIERS)
|
|
2017
|
+
):
|
|
2018
|
+
# Write a plain grouping's parentheses here rather than recursing through
|
|
2019
|
+
# subquery_sql: left-to-right chains nest one grouping per INTERSECT, and
|
|
2020
|
+
# long ones would exhaust the stack. The depth pushed here marks the close.
|
|
2021
|
+
open_parens += self.indent("(", level=depth, pad=0) + self.sep("")
|
|
2022
|
+
stack.append(depth)
|
|
2023
|
+
depth += 1
|
|
2024
|
+
left = left.this
|
|
2025
|
+
stack.append(left)
|
|
1969
2026
|
else:
|
|
1970
2027
|
if (
|
|
1971
2028
|
not self.SET_OP_LIMITS
|
|
@@ -1975,7 +2032,14 @@ class Generator:
|
|
|
1975
2032
|
node = node.subquery(copy=False)
|
|
1976
2033
|
if not self.SET_OP_PARENTHESIZED_OPERANDS:
|
|
1977
2034
|
node = exp.select("*").from_(node, copy=False)
|
|
1978
|
-
|
|
2035
|
+
elif (
|
|
2036
|
+
not self.SET_OP_PARENTHESIZED_OPERANDS
|
|
2037
|
+
and isinstance(node, exp.Subquery)
|
|
2038
|
+
and isinstance(node.unnest(), exp.SetOperation)
|
|
2039
|
+
):
|
|
2040
|
+
node = exp.select("*").from_(node, copy=False)
|
|
2041
|
+
sqls.append(open_parens + self.indent(self.sql(node), level=depth, pad=0))
|
|
2042
|
+
open_parens = ""
|
|
1979
2043
|
|
|
1980
2044
|
this = self.sep().join(sqls)
|
|
1981
2045
|
this = self.query_modifiers(expression, this)
|
|
@@ -2003,6 +2067,9 @@ class Generator:
|
|
|
2003
2067
|
where = self.sql(expression, "expression").strip()
|
|
2004
2068
|
return f"{this} FILTER({where})"
|
|
2005
2069
|
|
|
2070
|
+
def per_sql(self, expression: exp.Per) -> str:
|
|
2071
|
+
return f"{self.sql(expression, 'this')} PER ({self.expressions(expression, flat=True)})"
|
|
2072
|
+
|
|
2006
2073
|
def hint_sql(self, expression: exp.Hint) -> str:
|
|
2007
2074
|
if not self.QUERY_HINTS:
|
|
2008
2075
|
self.unsupported("Hints are not supported")
|
|
@@ -2024,7 +2091,9 @@ class Generator:
|
|
|
2024
2091
|
with_storage = self.expressions(expression, key="with_storage", flat=True)
|
|
2025
2092
|
with_storage = f" WITH ({with_storage})" if with_storage else ""
|
|
2026
2093
|
tablespace = self.sql(expression, "tablespace")
|
|
2027
|
-
|
|
2094
|
+
if tablespace:
|
|
2095
|
+
prefix = "" if isinstance(expression.parent, exp.Index) else " USING INDEX"
|
|
2096
|
+
tablespace = f"{prefix} TABLESPACE {tablespace}"
|
|
2028
2097
|
on = self.sql(expression, "on")
|
|
2029
2098
|
on = f" ON {on}" if on else ""
|
|
2030
2099
|
|
|
@@ -2168,7 +2237,7 @@ class Generator:
|
|
|
2168
2237
|
def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
|
|
2169
2238
|
if isinstance(expression.this, exp.Dot):
|
|
2170
2239
|
return self.sql(expression, "this")
|
|
2171
|
-
return
|
|
2240
|
+
return self.sql(exp.Literal.string(expression.name)) if string_key else expression.name
|
|
2172
2241
|
|
|
2173
2242
|
def property_sql(self, expression: exp.Property) -> str:
|
|
2174
2243
|
property_cls = expression.__class__
|
|
@@ -2911,6 +2980,12 @@ class Generator:
|
|
|
2911
2980
|
this = self.indent(self.sql(expression, "this"))
|
|
2912
2981
|
return f"{self.seg('HAVING')}{self.sep()}{this}"
|
|
2913
2982
|
|
|
2983
|
+
def connectto_sql(self, expression: exp.ConnectTo) -> str:
|
|
2984
|
+
return f"CONNECT {self.sql(expression, 'this')}"
|
|
2985
|
+
|
|
2986
|
+
def disconnect_sql(self, expression: exp.Disconnect) -> str:
|
|
2987
|
+
return "DISCONNECT"
|
|
2988
|
+
|
|
2914
2989
|
def connect_sql(self, expression: exp.Connect) -> str:
|
|
2915
2990
|
start = self.sql(expression, "start")
|
|
2916
2991
|
start = self.seg(f"START WITH {start}") if start else ""
|
|
@@ -2922,7 +2997,7 @@ class Generator:
|
|
|
2922
2997
|
def prior_sql(self, expression: exp.Prior) -> str:
|
|
2923
2998
|
return f"PRIOR {self.sql(expression, 'this')}"
|
|
2924
2999
|
|
|
2925
|
-
def join_sql(self, expression: exp.Join) -> str:
|
|
3000
|
+
def join_sql(self, expression: exp.Join, join_op: str | None = None) -> str:
|
|
2926
3001
|
if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
|
|
2927
3002
|
side = None
|
|
2928
3003
|
else:
|
|
@@ -2942,6 +3017,8 @@ class Generator:
|
|
|
2942
3017
|
)
|
|
2943
3018
|
match_cond = self.sql(expression, "match_condition")
|
|
2944
3019
|
match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
|
|
3020
|
+
nearest = self.sql(expression, "nearest")
|
|
3021
|
+
nearest = f" {nearest}" if nearest else ""
|
|
2945
3022
|
on_sql = self.sql(expression, "on")
|
|
2946
3023
|
using = expression.args.get("using")
|
|
2947
3024
|
|
|
@@ -2962,17 +3039,27 @@ class Generator:
|
|
|
2962
3039
|
on_sql = f"{space}USING ({on_sql})"
|
|
2963
3040
|
else:
|
|
2964
3041
|
on_sql = f"{space}ON {on_sql}"
|
|
2965
|
-
elif not op_sql:
|
|
3042
|
+
elif not op_sql and not nearest:
|
|
2966
3043
|
if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
|
|
2967
3044
|
return f" {this_sql}"
|
|
2968
3045
|
|
|
2969
3046
|
return f", {this_sql}"
|
|
2970
3047
|
|
|
2971
|
-
if
|
|
3048
|
+
if join_op:
|
|
3049
|
+
op_sql = join_op
|
|
3050
|
+
elif op_sql != "STRAIGHT_JOIN":
|
|
2972
3051
|
op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
|
|
2973
3052
|
|
|
2974
3053
|
pivots = self.expressions(expression, key="pivots", sep="", flat=True)
|
|
2975
|
-
return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"
|
|
3054
|
+
return f"{self.seg(op_sql)} {this_sql}{match_cond}{nearest}{on_sql}{pivots}"
|
|
3055
|
+
|
|
3056
|
+
def nearest_sql(self, expression: exp.Nearest) -> str:
|
|
3057
|
+
if not self.SUPPORTS_NEAREST_JOIN:
|
|
3058
|
+
self.unsupported("NEAREST joins are not supported")
|
|
3059
|
+
|
|
3060
|
+
approximate = "APPROX " if expression.args.get("approximate") else ""
|
|
3061
|
+
metric = "SIMILARITY" if expression.args.get("similarity") else "DISTANCE"
|
|
3062
|
+
return f"{approximate}NEAREST {self.sql(expression, 'count')} BY {metric} {self.sql(expression, 'this')}"
|
|
2976
3063
|
|
|
2977
3064
|
def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
|
|
2978
3065
|
args = self.expressions(expression, flat=True)
|
|
@@ -3636,6 +3723,9 @@ class Generator:
|
|
|
3636
3723
|
first = "FIRST" if first else "LAST"
|
|
3637
3724
|
|
|
3638
3725
|
if not partition and not order and not spec and alias:
|
|
3726
|
+
# WINDOW w2 AS w1 is unparseable in DuckDB/Postgres; parens are required.
|
|
3727
|
+
if expression.arg_key == "windows":
|
|
3728
|
+
return f"{this} ({alias})"
|
|
3639
3729
|
return f"{this} {alias}"
|
|
3640
3730
|
|
|
3641
3731
|
args = self.format_args(
|
|
@@ -4085,8 +4175,8 @@ class Generator:
|
|
|
4085
4175
|
if this:
|
|
4086
4176
|
interval_keyword = f"{interval_keyword} " if interval_keyword else ""
|
|
4087
4177
|
if unit_expression and isinstance(unit_expression, exp.IntervalSpan):
|
|
4088
|
-
return f"{interval_keyword}
|
|
4089
|
-
return f"{interval_keyword}'{this}{unit}'"
|
|
4178
|
+
return f"{interval_keyword}{self.sql(exp.Literal.string(this))}{unit}"
|
|
4179
|
+
return f"{interval_keyword}{self.sql(exp.Literal.string(f'{this}{unit}'))}"
|
|
4090
4180
|
return f"{interval_keyword}{unit}"
|
|
4091
4181
|
|
|
4092
4182
|
this = self.sql(expression, "this")
|
|
@@ -4508,13 +4598,17 @@ class Generator:
|
|
|
4508
4598
|
return f"{keyword} {items_sql}"
|
|
4509
4599
|
|
|
4510
4600
|
def add_column_sql(self, expression: exp.Expr) -> str:
|
|
4511
|
-
|
|
4512
|
-
if
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4601
|
+
column_text = ""
|
|
4602
|
+
if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
|
|
4603
|
+
if isinstance(expression, exp.ColumnDef):
|
|
4604
|
+
column_text = " COLUMN"
|
|
4605
|
+
elif isinstance(expression, exp.Schema):
|
|
4606
|
+
column_text = " COLUMNS"
|
|
4607
|
+
|
|
4608
|
+
if isinstance(expression, exp.Schema) and not self.ALTER_TABLE_ADD_COLUMNS_WRAPPED:
|
|
4609
|
+
sql = self.expressions(expression, flat=True)
|
|
4516
4610
|
else:
|
|
4517
|
-
|
|
4611
|
+
sql = self.sql(expression)
|
|
4518
4612
|
|
|
4519
4613
|
return f"ADD{column_text} {sql}"
|
|
4520
4614
|
|
|
@@ -4980,13 +5074,27 @@ class Generator:
|
|
|
4980
5074
|
|
|
4981
5075
|
then_expression = expression.args.get("then")
|
|
4982
5076
|
if isinstance(then_expression, exp.Insert):
|
|
4983
|
-
this =
|
|
5077
|
+
this = (
|
|
5078
|
+
"ALL BY NAME"
|
|
5079
|
+
if self.MERGE_ALL_BY_NAME and isinstance(then_expression.this, exp.Star)
|
|
5080
|
+
else self.sql(then_expression, "this")
|
|
5081
|
+
)
|
|
4984
5082
|
this = f"INSERT {this}" if this else "INSERT"
|
|
4985
5083
|
then = self.sql(then_expression, "expression")
|
|
4986
5084
|
then = f"{this} VALUES {then}" if then else this
|
|
4987
5085
|
elif isinstance(then_expression, exp.Update):
|
|
4988
5086
|
if isinstance(then_expression.args.get("expressions"), exp.Star):
|
|
4989
|
-
then =
|
|
5087
|
+
then = (
|
|
5088
|
+
"UPDATE ALL BY NAME"
|
|
5089
|
+
if self.MERGE_ALL_BY_NAME
|
|
5090
|
+
else f"UPDATE {self.sql(then_expression, 'expressions')}"
|
|
5091
|
+
)
|
|
5092
|
+
elif (
|
|
5093
|
+
self.MERGE_ALL_BY_NAME
|
|
5094
|
+
and len(then_expression.expressions) == 1
|
|
5095
|
+
and isinstance(then_expression.expressions[0], exp.Star)
|
|
5096
|
+
):
|
|
5097
|
+
then = "UPDATE ALL BY NAME"
|
|
4990
5098
|
else:
|
|
4991
5099
|
expressions_sql = self.expressions(then_expression)
|
|
4992
5100
|
then = f"UPDATE SET{self.sep()}{expressions_sql}" if expressions_sql else "UPDATE"
|
|
@@ -5947,7 +6055,8 @@ class Generator:
|
|
|
5947
6055
|
|
|
5948
6056
|
def slice_sql(self, expression: exp.Slice) -> str:
|
|
5949
6057
|
step = self.sql(expression, "step")
|
|
5950
|
-
end
|
|
6058
|
+
# An omitted end is written as `-` before a step, since `::` is the cast operator
|
|
6059
|
+
end = self.sql(expression.expression) or ("-" if step else "")
|
|
5951
6060
|
begin = self.sql(expression.this)
|
|
5952
6061
|
|
|
5953
6062
|
sql = f"{end}:{step}" if step else end
|
|
@@ -429,6 +429,7 @@ class BigQueryGenerator(generator.Generator):
|
|
|
429
429
|
exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"),
|
|
430
430
|
exp.Day: _extract_part("DAY"),
|
|
431
431
|
exp.DayOfMonth: _extract_part("DAY"),
|
|
432
|
+
exp.DayOfWeek: _extract_part("DAYOFWEEK"),
|
|
432
433
|
exp.DayOfWeekIso: _day_of_week_iso_sql,
|
|
433
434
|
exp.DayOfYear: _extract_part("DAYOFYEAR"),
|
|
434
435
|
exp.Filter: no_filter_sql,
|
|
@@ -24,6 +24,7 @@ from sqlglot.dialects.dialect import (
|
|
|
24
24
|
)
|
|
25
25
|
from sqlglot.generator import unsupported_args
|
|
26
26
|
from sqlglot.helper import is_int
|
|
27
|
+
from sqlglot.optimizer.simplify import simplify_parens
|
|
27
28
|
from collections import defaultdict
|
|
28
29
|
|
|
29
30
|
DATETIME_DELTA = t.Union[exp.DateAdd, exp.DateDiff, exp.DateSub, exp.TimestampSub, exp.TimestampAdd]
|
|
@@ -433,13 +434,12 @@ class ClickHouseGenerator(generator.Generator):
|
|
|
433
434
|
|
|
434
435
|
@unsupported_args("json_scope")
|
|
435
436
|
def contains_sql(self, expression: exp.Contains) -> str:
|
|
436
|
-
# ClickHouse has no CONTAINS; the
|
|
437
|
-
result = exp.
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
return self.sql(result)
|
|
437
|
+
# ClickHouse has no CONTAINS; keep the parentheses only where the parent needs them
|
|
438
|
+
result = exp.paren(
|
|
439
|
+
exp.StrPosition(this=expression.this, substr=expression.expression) > 0, copy=False
|
|
440
|
+
)
|
|
441
|
+
result.parent = expression.parent
|
|
442
|
+
return self.sql(simplify_parens(result, self.dialect))
|
|
443
443
|
|
|
444
444
|
def groupconcat_sql(self, expression: exp.GroupConcat) -> str:
|
|
445
445
|
this = expression.this
|