sqlglot 28.4.1__py3-none-any.whl → 28.8.0__py3-none-any.whl
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.
- sqlglot/_version.py +2 -2
- sqlglot/dialects/bigquery.py +20 -23
- sqlglot/dialects/clickhouse.py +2 -0
- sqlglot/dialects/dialect.py +355 -18
- sqlglot/dialects/doris.py +38 -90
- sqlglot/dialects/druid.py +1 -0
- sqlglot/dialects/duckdb.py +1739 -163
- sqlglot/dialects/exasol.py +17 -1
- sqlglot/dialects/hive.py +27 -2
- sqlglot/dialects/mysql.py +103 -11
- sqlglot/dialects/oracle.py +38 -1
- sqlglot/dialects/postgres.py +142 -33
- sqlglot/dialects/presto.py +6 -2
- sqlglot/dialects/redshift.py +7 -1
- sqlglot/dialects/singlestore.py +13 -3
- sqlglot/dialects/snowflake.py +271 -21
- sqlglot/dialects/spark.py +25 -0
- sqlglot/dialects/spark2.py +4 -3
- sqlglot/dialects/starrocks.py +152 -17
- sqlglot/dialects/trino.py +1 -0
- sqlglot/dialects/tsql.py +5 -0
- sqlglot/diff.py +1 -1
- sqlglot/expressions.py +239 -47
- sqlglot/generator.py +173 -44
- sqlglot/optimizer/annotate_types.py +129 -60
- sqlglot/optimizer/merge_subqueries.py +13 -2
- sqlglot/optimizer/qualify_columns.py +7 -0
- sqlglot/optimizer/resolver.py +19 -0
- sqlglot/optimizer/scope.py +12 -0
- sqlglot/optimizer/unnest_subqueries.py +7 -0
- sqlglot/parser.py +251 -58
- sqlglot/schema.py +186 -14
- sqlglot/tokens.py +36 -6
- sqlglot/transforms.py +6 -5
- sqlglot/typing/__init__.py +29 -10
- sqlglot/typing/bigquery.py +5 -10
- sqlglot/typing/duckdb.py +39 -0
- sqlglot/typing/hive.py +50 -1
- sqlglot/typing/mysql.py +32 -0
- sqlglot/typing/presto.py +0 -1
- sqlglot/typing/snowflake.py +80 -17
- sqlglot/typing/spark.py +29 -0
- sqlglot/typing/spark2.py +9 -1
- sqlglot/typing/tsql.py +21 -0
- {sqlglot-28.4.1.dist-info → sqlglot-28.8.0.dist-info}/METADATA +47 -2
- sqlglot-28.8.0.dist-info/RECORD +95 -0
- {sqlglot-28.4.1.dist-info → sqlglot-28.8.0.dist-info}/WHEEL +1 -1
- sqlglot-28.4.1.dist-info/RECORD +0 -92
- {sqlglot-28.4.1.dist-info → sqlglot-28.8.0.dist-info}/licenses/LICENSE +0 -0
- {sqlglot-28.4.1.dist-info → sqlglot-28.8.0.dist-info}/top_level.txt +0 -0
sqlglot/dialects/starrocks.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import typing as t
|
|
4
4
|
|
|
5
|
-
from sqlglot import exp
|
|
5
|
+
from sqlglot import exp, transforms
|
|
6
6
|
from sqlglot.dialects.dialect import (
|
|
7
7
|
approx_count_distinct_sql,
|
|
8
8
|
arrow_json_extract_sql,
|
|
@@ -17,6 +17,31 @@ from sqlglot.helper import seq_get
|
|
|
17
17
|
from sqlglot.tokens import TokenType
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def _eliminate_between_in_delete(expression: exp.Expression) -> exp.Expression:
|
|
21
|
+
"""
|
|
22
|
+
StarRocks doesn't support BETWEEN in DELETE statements, so we convert
|
|
23
|
+
BETWEEN expressions to explicit comparisons.
|
|
24
|
+
|
|
25
|
+
https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/DELETE/#parameters
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
>>> from sqlglot import parse_one
|
|
29
|
+
>>> expr = parse_one("DELETE FROM t WHERE x BETWEEN 1 AND 10")
|
|
30
|
+
>>> print(_eliminate_between_in_delete(expr).sql(dialect="starrocks"))
|
|
31
|
+
DELETE FROM t WHERE x >= 1 AND x <= 10
|
|
32
|
+
"""
|
|
33
|
+
if where := expression.args.get("where"):
|
|
34
|
+
for between in where.find_all(exp.Between):
|
|
35
|
+
between.replace(
|
|
36
|
+
exp.and_(
|
|
37
|
+
exp.GTE(this=between.this.copy(), expression=between.args["low"]),
|
|
38
|
+
exp.LTE(this=between.this.copy(), expression=between.args["high"]),
|
|
39
|
+
copy=False,
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
return expression
|
|
43
|
+
|
|
44
|
+
|
|
20
45
|
# https://docs.starrocks.io/docs/sql-reference/sql-functions/spatial-functions/st_distance_sphere/
|
|
21
46
|
def st_distance_sphere(self, expression: exp.StDistance) -> str:
|
|
22
47
|
point1 = expression.this
|
|
@@ -56,11 +81,31 @@ class StarRocks(MySQL):
|
|
|
56
81
|
|
|
57
82
|
PROPERTY_PARSERS = {
|
|
58
83
|
**MySQL.Parser.PROPERTY_PARSERS,
|
|
59
|
-
"PARTITION BY": lambda self: self._parse_partition_by_opt_range(),
|
|
60
84
|
"PROPERTIES": lambda self: self._parse_wrapped_properties(),
|
|
61
85
|
"UNIQUE": lambda self: self._parse_composite_key_property(exp.UniqueKeyProperty),
|
|
86
|
+
"ROLLUP": lambda self: self._parse_rollup_property(),
|
|
87
|
+
"REFRESH": lambda self: self._parse_refresh_property(),
|
|
62
88
|
}
|
|
63
89
|
|
|
90
|
+
def _parse_rollup_property(self) -> exp.RollupProperty:
|
|
91
|
+
# ROLLUP (rollup_name (col1, col2) [FROM from_index] [PROPERTIES (...)], ...)
|
|
92
|
+
def parse_rollup_index() -> exp.RollupIndex:
|
|
93
|
+
return self.expression(
|
|
94
|
+
exp.RollupIndex,
|
|
95
|
+
this=self._parse_id_var(),
|
|
96
|
+
expressions=self._parse_wrapped_id_vars(),
|
|
97
|
+
from_index=self._parse_id_var() if self._match_text_seq("FROM") else None,
|
|
98
|
+
properties=self.expression(
|
|
99
|
+
exp.Properties, expressions=self._parse_wrapped_properties()
|
|
100
|
+
)
|
|
101
|
+
if self._match_text_seq("PROPERTIES")
|
|
102
|
+
else None,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return self.expression(
|
|
106
|
+
exp.RollupProperty, expressions=self._parse_wrapped_csv(parse_rollup_index)
|
|
107
|
+
)
|
|
108
|
+
|
|
64
109
|
def _parse_create(self) -> exp.Create | exp.Command:
|
|
65
110
|
create = super()._parse_create()
|
|
66
111
|
|
|
@@ -94,6 +139,40 @@ class StarRocks(MySQL):
|
|
|
94
139
|
|
|
95
140
|
return unnest
|
|
96
141
|
|
|
142
|
+
def _parse_partitioned_by(self) -> exp.PartitionedByProperty:
|
|
143
|
+
return self.expression(
|
|
144
|
+
exp.PartitionedByProperty,
|
|
145
|
+
this=exp.Schema(
|
|
146
|
+
expressions=self._parse_wrapped_csv(self._parse_assignment, optional=True)
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def _parse_partition_property(
|
|
151
|
+
self,
|
|
152
|
+
) -> t.Optional[exp.Expression] | t.List[exp.Expression]:
|
|
153
|
+
expr = super()._parse_partition_property()
|
|
154
|
+
|
|
155
|
+
if not expr:
|
|
156
|
+
return self._parse_partitioned_by()
|
|
157
|
+
|
|
158
|
+
if isinstance(expr, exp.Property):
|
|
159
|
+
return expr
|
|
160
|
+
|
|
161
|
+
self._match_l_paren()
|
|
162
|
+
|
|
163
|
+
if self._match_text_seq("START", advance=False):
|
|
164
|
+
create_expressions = self._parse_csv(self._parse_partitioning_granularity_dynamic)
|
|
165
|
+
else:
|
|
166
|
+
create_expressions = None
|
|
167
|
+
|
|
168
|
+
self._match_r_paren()
|
|
169
|
+
|
|
170
|
+
return self.expression(
|
|
171
|
+
exp.PartitionByRangeProperty,
|
|
172
|
+
partition_expressions=expr,
|
|
173
|
+
create_expressions=create_expressions,
|
|
174
|
+
)
|
|
175
|
+
|
|
97
176
|
def _parse_partitioning_granularity_dynamic(self) -> exp.PartitionByRangePropertyDynamic:
|
|
98
177
|
self._match_text_seq("START")
|
|
99
178
|
start = self._parse_wrapped(self._parse_string)
|
|
@@ -105,20 +184,33 @@ class StarRocks(MySQL):
|
|
|
105
184
|
exp.PartitionByRangePropertyDynamic, start=start, end=end, every=every
|
|
106
185
|
)
|
|
107
186
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
187
|
+
def _parse_refresh_property(self) -> exp.RefreshTriggerProperty:
|
|
188
|
+
"""
|
|
189
|
+
REFRESH [DEFERRED | IMMEDIATE]
|
|
190
|
+
[ASYNC | ASYNC [START (<start_time>)] EVERY (INTERVAL <refresh_interval>) | MANUAL]
|
|
191
|
+
"""
|
|
192
|
+
method = self._match_texts(("DEFERRED", "IMMEDIATE")) and self._prev.text.upper()
|
|
193
|
+
kind = self._match_texts(("ASYNC", "MANUAL")) and self._prev.text.upper()
|
|
194
|
+
start = self._match_text_seq("START") and self._parse_wrapped(self._parse_string)
|
|
195
|
+
|
|
196
|
+
if self._match_text_seq("EVERY"):
|
|
197
|
+
self._match_l_paren()
|
|
198
|
+
self._match_text_seq("INTERVAL")
|
|
199
|
+
every = self._parse_number()
|
|
200
|
+
unit = self._parse_var(any_token=True)
|
|
201
|
+
self._match_r_paren()
|
|
202
|
+
else:
|
|
203
|
+
every = None
|
|
204
|
+
unit = None
|
|
205
|
+
|
|
206
|
+
return self.expression(
|
|
207
|
+
exp.RefreshTriggerProperty,
|
|
208
|
+
method=method,
|
|
209
|
+
kind=kind,
|
|
210
|
+
starts=start,
|
|
211
|
+
every=every,
|
|
212
|
+
unit=unit,
|
|
213
|
+
)
|
|
122
214
|
|
|
123
215
|
class Generator(MySQL.Generator):
|
|
124
216
|
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
|
|
@@ -126,9 +218,13 @@ class StarRocks(MySQL):
|
|
|
126
218
|
VARCHAR_REQUIRES_SIZE = False
|
|
127
219
|
PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
|
|
128
220
|
WITH_PROPERTIES_PREFIX = "PROPERTIES"
|
|
221
|
+
UPDATE_STATEMENT_SUPPORTS_FROM = True
|
|
222
|
+
INSERT_OVERWRITE = " OVERWRITE"
|
|
129
223
|
|
|
130
224
|
# StarRocks doesn't support "IS TRUE/FALSE" syntax.
|
|
131
225
|
IS_BOOL_ALLOWED = False
|
|
226
|
+
# StarRocks doesn't support renaming a table with a database.
|
|
227
|
+
RENAME_TABLE_WITH_DB = False
|
|
132
228
|
|
|
133
229
|
CAST_MAPPING = {}
|
|
134
230
|
|
|
@@ -144,7 +240,8 @@ class StarRocks(MySQL):
|
|
|
144
240
|
**MySQL.Generator.PROPERTIES_LOCATION,
|
|
145
241
|
exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
|
|
146
242
|
exp.UniqueKeyProperty: exp.Properties.Location.POST_SCHEMA,
|
|
147
|
-
exp.
|
|
243
|
+
exp.RollupProperty: exp.Properties.Location.POST_SCHEMA,
|
|
244
|
+
exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA,
|
|
148
245
|
}
|
|
149
246
|
|
|
150
247
|
TRANSFORMS = {
|
|
@@ -157,6 +254,7 @@ class StarRocks(MySQL):
|
|
|
157
254
|
exp.DateDiff: lambda self, e: self.func(
|
|
158
255
|
"DATE_DIFF", unit_to_str(e), e.this, e.expression
|
|
159
256
|
),
|
|
257
|
+
exp.Delete: transforms.preprocess([_eliminate_between_in_delete]),
|
|
160
258
|
exp.Flatten: rename_func("ARRAY_FLATTEN"),
|
|
161
259
|
exp.JSONExtractScalar: arrow_json_extract_sql,
|
|
162
260
|
exp.JSONExtract: arrow_json_extract_sql,
|
|
@@ -348,3 +446,40 @@ class StarRocks(MySQL):
|
|
|
348
446
|
props.set("expressions", primary_key.pop(), engine_index + 1, overwrite=False)
|
|
349
447
|
|
|
350
448
|
return super().create_sql(expression)
|
|
449
|
+
|
|
450
|
+
def partitionedbyproperty_sql(self, expression: exp.PartitionedByProperty) -> str:
|
|
451
|
+
this = expression.this
|
|
452
|
+
if isinstance(this, exp.Schema):
|
|
453
|
+
# For MVs, StarRocks needs outer parentheses.
|
|
454
|
+
create = expression.find_ancestor(exp.Create)
|
|
455
|
+
|
|
456
|
+
sql = self.expressions(this, flat=True)
|
|
457
|
+
if (create and create.kind == "VIEW") or all(
|
|
458
|
+
isinstance(col, (exp.Column, exp.Identifier)) for col in this.expressions
|
|
459
|
+
):
|
|
460
|
+
sql = f"({sql})"
|
|
461
|
+
|
|
462
|
+
return f"PARTITION BY {sql}"
|
|
463
|
+
|
|
464
|
+
return f"PARTITION BY {self.sql(this)}"
|
|
465
|
+
|
|
466
|
+
def cluster_sql(self, expression: exp.Cluster) -> str:
|
|
467
|
+
"""Generate StarRocks ORDER BY clause for clustering."""
|
|
468
|
+
expressions = self.expressions(expression, flat=True)
|
|
469
|
+
return f"ORDER BY ({expressions})" if expressions else ""
|
|
470
|
+
|
|
471
|
+
def refreshtriggerproperty_sql(self, expression: exp.RefreshTriggerProperty) -> str:
|
|
472
|
+
"""Generate StarRocks REFRESH clause for materialized views.
|
|
473
|
+
There is a little difference of the syntax between StarRocks and Doris.
|
|
474
|
+
"""
|
|
475
|
+
method = self.sql(expression, "method")
|
|
476
|
+
method = f" {method}" if method else ""
|
|
477
|
+
kind = self.sql(expression, "kind")
|
|
478
|
+
kind = f" {kind}" if kind else ""
|
|
479
|
+
starts = self.sql(expression, "starts")
|
|
480
|
+
starts = f" START ({starts})" if starts else ""
|
|
481
|
+
every = self.sql(expression, "every")
|
|
482
|
+
unit = self.sql(expression, "unit")
|
|
483
|
+
every = f" EVERY (INTERVAL {every} {unit})" if every and unit else ""
|
|
484
|
+
|
|
485
|
+
return f"REFRESH{method}{kind}{starts}{every}"
|
sqlglot/dialects/trino.py
CHANGED
sqlglot/dialects/tsql.py
CHANGED
|
@@ -601,6 +601,7 @@ class TSQL(Dialect):
|
|
|
601
601
|
|
|
602
602
|
FUNCTIONS = {
|
|
603
603
|
**parser.Parser.FUNCTIONS,
|
|
604
|
+
"ATN2": exp.Atan2.from_arg_list,
|
|
604
605
|
"CHARINDEX": lambda args: exp.StrPosition(
|
|
605
606
|
this=seq_get(args, 1),
|
|
606
607
|
substr=seq_get(args, 0),
|
|
@@ -989,6 +990,9 @@ class TSQL(Dialect):
|
|
|
989
990
|
|
|
990
991
|
return expression
|
|
991
992
|
|
|
993
|
+
def _parse_primary_key_part(self) -> t.Optional[exp.Expression]:
|
|
994
|
+
return self._parse_ordered()
|
|
995
|
+
|
|
992
996
|
class Generator(generator.Generator):
|
|
993
997
|
LIMIT_IS_TOP = True
|
|
994
998
|
QUERY_HINTS = False
|
|
@@ -1055,6 +1059,7 @@ class TSQL(Dialect):
|
|
|
1055
1059
|
TRANSFORMS = {
|
|
1056
1060
|
**generator.Generator.TRANSFORMS,
|
|
1057
1061
|
exp.AnyValue: any_value_to_max_sql,
|
|
1062
|
+
exp.Atan2: rename_func("ATN2"),
|
|
1058
1063
|
exp.ArrayToString: rename_func("STRING_AGG"),
|
|
1059
1064
|
exp.AutoIncrementColumnConstraint: lambda *_: "IDENTITY",
|
|
1060
1065
|
exp.Ceil: rename_func("CEILING"),
|
sqlglot/diff.py
CHANGED
|
@@ -181,7 +181,7 @@ class ChangeDistiller:
|
|
|
181
181
|
def __init__(self, f: float = 0.6, t: float = 0.6, dialect: DialectType = None) -> None:
|
|
182
182
|
self.f = f
|
|
183
183
|
self.t = t
|
|
184
|
-
self._sql_generator = Dialect.get_or_raise(dialect).generator()
|
|
184
|
+
self._sql_generator = Dialect.get_or_raise(dialect).generator(comments=False)
|
|
185
185
|
|
|
186
186
|
def diff(
|
|
187
187
|
self,
|