sqlglot 27.6.0__py3-none-any.whl → 27.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 +16 -3
- sqlglot/dialects/bigquery.py +57 -8
- sqlglot/dialects/clickhouse.py +27 -8
- sqlglot/dialects/databricks.py +5 -1
- sqlglot/dialects/dialect.py +82 -25
- sqlglot/dialects/doris.py +43 -2
- sqlglot/dialects/dremio.py +53 -11
- sqlglot/dialects/duckdb.py +39 -43
- sqlglot/dialects/exasol.py +30 -29
- sqlglot/dialects/mysql.py +0 -15
- sqlglot/dialects/presto.py +0 -2
- sqlglot/dialects/singlestore.py +121 -1
- sqlglot/dialects/snowflake.py +1 -1
- sqlglot/dialects/spark.py +7 -0
- sqlglot/dialects/teradata.py +58 -0
- sqlglot/dialects/trino.py +1 -0
- sqlglot/expressions.py +86 -4
- sqlglot/generator.py +27 -3
- sqlglot/jsonpath.py +10 -3
- sqlglot/optimizer/annotate_types.py +4 -1
- sqlglot/optimizer/merge_subqueries.py +4 -0
- sqlglot/optimizer/qualify_tables.py +0 -8
- sqlglot/parser.py +44 -6
- {sqlglot-27.6.0.dist-info → sqlglot-27.8.0.dist-info}/METADATA +2 -2
- {sqlglot-27.6.0.dist-info → sqlglot-27.8.0.dist-info}/RECORD +28 -28
- {sqlglot-27.6.0.dist-info → sqlglot-27.8.0.dist-info}/WHEEL +0 -0
- {sqlglot-27.6.0.dist-info → sqlglot-27.8.0.dist-info}/licenses/LICENSE +0 -0
- {sqlglot-27.6.0.dist-info → sqlglot-27.8.0.dist-info}/top_level.txt +0 -0
sqlglot/dialects/teradata.py
CHANGED
|
@@ -90,6 +90,7 @@ class Teradata(Dialect):
|
|
|
90
90
|
"HELP": TokenType.COMMAND,
|
|
91
91
|
"INS": TokenType.INSERT,
|
|
92
92
|
"LE": TokenType.LTE,
|
|
93
|
+
"LOCKING": TokenType.LOCK,
|
|
93
94
|
"LT": TokenType.LT,
|
|
94
95
|
"MINUS": TokenType.EXCEPT,
|
|
95
96
|
"MOD": TokenType.MOD,
|
|
@@ -155,6 +156,26 @@ class Teradata(Dialect):
|
|
|
155
156
|
exp.Use, this=self._parse_table(schema=False)
|
|
156
157
|
),
|
|
157
158
|
TokenType.REPLACE: lambda self: self._parse_create(),
|
|
159
|
+
TokenType.LOCK: lambda self: self._parse_locking_statement(),
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def _parse_locking_statement(self) -> exp.LockingStatement:
|
|
163
|
+
# Reuse exp.LockingProperty parsing for the lock kind, type etc
|
|
164
|
+
locking_property = self._parse_locking()
|
|
165
|
+
wrapped_query = self._parse_select()
|
|
166
|
+
|
|
167
|
+
if not wrapped_query:
|
|
168
|
+
self.raise_error("Expected SELECT statement after LOCKING clause")
|
|
169
|
+
|
|
170
|
+
return self.expression(
|
|
171
|
+
exp.LockingStatement,
|
|
172
|
+
this=locking_property,
|
|
173
|
+
expression=wrapped_query,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
SET_PARSERS = {
|
|
177
|
+
**parser.Parser.SET_PARSERS,
|
|
178
|
+
"QUERY_BAND": lambda self: self._parse_query_band(),
|
|
158
179
|
}
|
|
159
180
|
|
|
160
181
|
FUNCTION_PARSERS = {
|
|
@@ -210,6 +231,36 @@ class Teradata(Dialect):
|
|
|
210
231
|
|
|
211
232
|
return self.expression(exp.RangeN, this=this, expressions=expressions, each=each)
|
|
212
233
|
|
|
234
|
+
def _parse_query_band(self) -> exp.QueryBand:
|
|
235
|
+
# Parse: SET QUERY_BAND = 'key=value;key2=value2;' FOR SESSION|TRANSACTION
|
|
236
|
+
# Also supports: SET QUERY_BAND = 'key=value;' UPDATE FOR SESSION|TRANSACTION
|
|
237
|
+
# Also supports: SET QUERY_BAND = NONE FOR SESSION|TRANSACTION
|
|
238
|
+
self._match(TokenType.EQ)
|
|
239
|
+
|
|
240
|
+
# Handle both string literals and NONE keyword
|
|
241
|
+
if self._match_text_seq("NONE"):
|
|
242
|
+
query_band_string: t.Optional[exp.Expression] = exp.Var(this="NONE")
|
|
243
|
+
else:
|
|
244
|
+
query_band_string = self._parse_string()
|
|
245
|
+
|
|
246
|
+
update = self._match_text_seq("UPDATE")
|
|
247
|
+
self._match_text_seq("FOR")
|
|
248
|
+
|
|
249
|
+
# Handle scope - can be SESSION, TRANSACTION, VOLATILE, or SESSION VOLATILE
|
|
250
|
+
if self._match_text_seq("SESSION", "VOLATILE"):
|
|
251
|
+
scope = "SESSION VOLATILE"
|
|
252
|
+
elif self._match_texts(("SESSION", "TRANSACTION")):
|
|
253
|
+
scope = self._prev.text.upper()
|
|
254
|
+
else:
|
|
255
|
+
scope = None
|
|
256
|
+
|
|
257
|
+
return self.expression(
|
|
258
|
+
exp.QueryBand,
|
|
259
|
+
this=query_band_string,
|
|
260
|
+
scope=scope,
|
|
261
|
+
update=update,
|
|
262
|
+
)
|
|
263
|
+
|
|
213
264
|
def _parse_index_params(self) -> exp.IndexParameters:
|
|
214
265
|
this = super()._parse_index_params()
|
|
215
266
|
|
|
@@ -358,6 +409,13 @@ class Teradata(Dialect):
|
|
|
358
409
|
|
|
359
410
|
return f"RANGE_N({this} BETWEEN {expressions_sql}{each_sql})"
|
|
360
411
|
|
|
412
|
+
def lockingstatement_sql(self, expression: exp.LockingStatement) -> str:
|
|
413
|
+
"""Generate SQL for LOCKING statement"""
|
|
414
|
+
locking_clause = self.sql(expression, "this")
|
|
415
|
+
query_sql = self.sql(expression, "expression")
|
|
416
|
+
|
|
417
|
+
return f"{locking_clause} {query_sql}"
|
|
418
|
+
|
|
361
419
|
def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
|
|
362
420
|
kind = self.sql(expression, "kind").upper()
|
|
363
421
|
if kind == "TABLE" and locations.get(exp.Properties.Location.POST_NAME):
|
sqlglot/dialects/trino.py
CHANGED
|
@@ -21,6 +21,7 @@ class Trino(Presto):
|
|
|
21
21
|
**Presto.Parser.FUNCTION_PARSERS,
|
|
22
22
|
"TRIM": lambda self: self._parse_trim(),
|
|
23
23
|
"JSON_QUERY": lambda self: self._parse_json_query(),
|
|
24
|
+
"JSON_VALUE": lambda self: self._parse_json_value(),
|
|
24
25
|
"LISTAGG": lambda self: self._parse_string_agg(),
|
|
25
26
|
}
|
|
26
27
|
|
sqlglot/expressions.py
CHANGED
|
@@ -1458,6 +1458,11 @@ class DDL(Expression):
|
|
|
1458
1458
|
return self.expression.named_selects if isinstance(self.expression, Query) else []
|
|
1459
1459
|
|
|
1460
1460
|
|
|
1461
|
+
# https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Manipulation-Language/Statement-Syntax/LOCKING-Request-Modifier/LOCKING-Request-Modifier-Syntax
|
|
1462
|
+
class LockingStatement(Expression):
|
|
1463
|
+
arg_types = {"this": True, "expression": True}
|
|
1464
|
+
|
|
1465
|
+
|
|
1461
1466
|
class DML(Expression):
|
|
1462
1467
|
def returning(
|
|
1463
1468
|
self,
|
|
@@ -1613,6 +1618,10 @@ class SetItem(Expression):
|
|
|
1613
1618
|
}
|
|
1614
1619
|
|
|
1615
1620
|
|
|
1621
|
+
class QueryBand(Expression):
|
|
1622
|
+
arg_types = {"this": True, "scope": False, "update": False}
|
|
1623
|
+
|
|
1624
|
+
|
|
1616
1625
|
class Show(Expression):
|
|
1617
1626
|
arg_types = {
|
|
1618
1627
|
"this": True,
|
|
@@ -1680,7 +1689,7 @@ class ProjectionDef(Expression):
|
|
|
1680
1689
|
|
|
1681
1690
|
|
|
1682
1691
|
class TableAlias(Expression):
|
|
1683
|
-
arg_types = {"this": False, "columns": False
|
|
1692
|
+
arg_types = {"this": False, "columns": False}
|
|
1684
1693
|
|
|
1685
1694
|
@property
|
|
1686
1695
|
def columns(self):
|
|
@@ -5292,7 +5301,7 @@ class TimeUnit(Expression):
|
|
|
5292
5301
|
|
|
5293
5302
|
def __init__(self, **args):
|
|
5294
5303
|
unit = args.get("unit")
|
|
5295
|
-
if
|
|
5304
|
+
if type(unit) in self.VAR_LIKE:
|
|
5296
5305
|
args["unit"] = Var(
|
|
5297
5306
|
this=(self.UNABBREVIATED_UNIT_NAME.get(unit.name) or unit.name).upper()
|
|
5298
5307
|
)
|
|
@@ -5416,6 +5425,10 @@ class BitwiseCountAgg(AggFunc):
|
|
|
5416
5425
|
_sql_names = ["BIT_COUNT"]
|
|
5417
5426
|
|
|
5418
5427
|
|
|
5428
|
+
class ByteLength(Func):
|
|
5429
|
+
pass
|
|
5430
|
+
|
|
5431
|
+
|
|
5419
5432
|
class ArrayRemove(Func):
|
|
5420
5433
|
arg_types = {"this": True, "expression": True}
|
|
5421
5434
|
|
|
@@ -5451,6 +5464,15 @@ class Transform(Func):
|
|
|
5451
5464
|
arg_types = {"this": True, "expression": True}
|
|
5452
5465
|
|
|
5453
5466
|
|
|
5467
|
+
class Translate(Func):
|
|
5468
|
+
arg_types = {"this": True, "from": True, "to": True}
|
|
5469
|
+
|
|
5470
|
+
|
|
5471
|
+
class Grouping(AggFunc):
|
|
5472
|
+
arg_types = {"expressions": True}
|
|
5473
|
+
is_var_len_args = True
|
|
5474
|
+
|
|
5475
|
+
|
|
5454
5476
|
class Anonymous(Func):
|
|
5455
5477
|
arg_types = {"this": True, "expressions": False}
|
|
5456
5478
|
is_var_len_args = True
|
|
@@ -5518,7 +5540,12 @@ class Pad(Func):
|
|
|
5518
5540
|
# https://docs.snowflake.com/en/sql-reference/functions/to_char
|
|
5519
5541
|
# https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/TO_CHAR-number.html
|
|
5520
5542
|
class ToChar(Func):
|
|
5521
|
-
arg_types = {
|
|
5543
|
+
arg_types = {
|
|
5544
|
+
"this": True,
|
|
5545
|
+
"format": False,
|
|
5546
|
+
"nlsparam": False,
|
|
5547
|
+
"is_numeric": False,
|
|
5548
|
+
}
|
|
5522
5549
|
|
|
5523
5550
|
|
|
5524
5551
|
# https://docs.snowflake.com/en/sql-reference/functions/to_decimal
|
|
@@ -5564,6 +5591,10 @@ class ConvertTimezone(Func):
|
|
|
5564
5591
|
}
|
|
5565
5592
|
|
|
5566
5593
|
|
|
5594
|
+
class CodePointsToString(Func):
|
|
5595
|
+
pass
|
|
5596
|
+
|
|
5597
|
+
|
|
5567
5598
|
class GenerateSeries(Func):
|
|
5568
5599
|
arg_types = {"start": True, "end": True, "step": False, "is_end_exclusive": False}
|
|
5569
5600
|
|
|
@@ -5791,6 +5822,18 @@ class JSONCast(Cast):
|
|
|
5791
5822
|
pass
|
|
5792
5823
|
|
|
5793
5824
|
|
|
5825
|
+
class JustifyDays(Func):
|
|
5826
|
+
pass
|
|
5827
|
+
|
|
5828
|
+
|
|
5829
|
+
class JustifyHours(Func):
|
|
5830
|
+
pass
|
|
5831
|
+
|
|
5832
|
+
|
|
5833
|
+
class JustifyInterval(Func):
|
|
5834
|
+
pass
|
|
5835
|
+
|
|
5836
|
+
|
|
5794
5837
|
class Try(Func):
|
|
5795
5838
|
pass
|
|
5796
5839
|
|
|
@@ -5947,6 +5990,10 @@ class DatetimeTrunc(Func, TimeUnit):
|
|
|
5947
5990
|
arg_types = {"this": True, "unit": True, "zone": False}
|
|
5948
5991
|
|
|
5949
5992
|
|
|
5993
|
+
class DateFromUnixDate(Func):
|
|
5994
|
+
pass
|
|
5995
|
+
|
|
5996
|
+
|
|
5950
5997
|
class DayOfWeek(Func):
|
|
5951
5998
|
_sql_names = ["DAY_OF_WEEK", "DAYOFWEEK"]
|
|
5952
5999
|
|
|
@@ -6475,7 +6522,13 @@ class JSONExtractArray(Func):
|
|
|
6475
6522
|
|
|
6476
6523
|
|
|
6477
6524
|
class JSONExtractScalar(Binary, Func):
|
|
6478
|
-
arg_types = {
|
|
6525
|
+
arg_types = {
|
|
6526
|
+
"this": True,
|
|
6527
|
+
"expression": True,
|
|
6528
|
+
"only_json_types": False,
|
|
6529
|
+
"expressions": False,
|
|
6530
|
+
"json_type": False,
|
|
6531
|
+
}
|
|
6479
6532
|
_sql_names = ["JSON_EXTRACT_SCALAR"]
|
|
6480
6533
|
is_var_len_args = True
|
|
6481
6534
|
|
|
@@ -6489,6 +6542,7 @@ class JSONBExtract(Binary, Func):
|
|
|
6489
6542
|
|
|
6490
6543
|
|
|
6491
6544
|
class JSONBExtractScalar(Binary, Func):
|
|
6545
|
+
arg_types = {"this": True, "expression": True, "json_type": False}
|
|
6492
6546
|
_sql_names = ["JSONB_EXTRACT_SCALAR"]
|
|
6493
6547
|
|
|
6494
6548
|
|
|
@@ -6509,6 +6563,14 @@ class ParseJSON(Func):
|
|
|
6509
6563
|
arg_types = {"this": True, "expression": False, "safe": False}
|
|
6510
6564
|
|
|
6511
6565
|
|
|
6566
|
+
class ParseTime(Func):
|
|
6567
|
+
arg_types = {"this": True, "format": True}
|
|
6568
|
+
|
|
6569
|
+
|
|
6570
|
+
class ParseDatetime(Func):
|
|
6571
|
+
arg_types = {"this": True, "format": False, "zone": False}
|
|
6572
|
+
|
|
6573
|
+
|
|
6512
6574
|
class Least(Func):
|
|
6513
6575
|
arg_types = {"this": True, "expressions": False}
|
|
6514
6576
|
is_var_len_args = True
|
|
@@ -6522,6 +6584,10 @@ class Right(Func):
|
|
|
6522
6584
|
arg_types = {"this": True, "expression": True}
|
|
6523
6585
|
|
|
6524
6586
|
|
|
6587
|
+
class Reverse(Func):
|
|
6588
|
+
pass
|
|
6589
|
+
|
|
6590
|
+
|
|
6525
6591
|
class Length(Func):
|
|
6526
6592
|
arg_types = {"this": True, "binary": False, "encoding": False}
|
|
6527
6593
|
_sql_names = ["LENGTH", "LEN", "CHAR_LENGTH", "CHARACTER_LENGTH"]
|
|
@@ -6796,6 +6862,10 @@ class SortArray(Func):
|
|
|
6796
6862
|
arg_types = {"this": True, "asc": False}
|
|
6797
6863
|
|
|
6798
6864
|
|
|
6865
|
+
class Soundex(Func):
|
|
6866
|
+
pass
|
|
6867
|
+
|
|
6868
|
+
|
|
6799
6869
|
class Split(Func):
|
|
6800
6870
|
arg_types = {"this": True, "expression": True, "limit": False}
|
|
6801
6871
|
|
|
@@ -7047,6 +7117,14 @@ class UnixSeconds(Func):
|
|
|
7047
7117
|
pass
|
|
7048
7118
|
|
|
7049
7119
|
|
|
7120
|
+
class UnixMicros(Func):
|
|
7121
|
+
pass
|
|
7122
|
+
|
|
7123
|
+
|
|
7124
|
+
class UnixMillis(Func):
|
|
7125
|
+
pass
|
|
7126
|
+
|
|
7127
|
+
|
|
7050
7128
|
class Uuid(Func):
|
|
7051
7129
|
_sql_names = ["UUID", "GEN_RANDOM_UUID", "GENERATE_UUID", "UUID_STRING"]
|
|
7052
7130
|
|
|
@@ -7096,6 +7174,10 @@ class Week(Func):
|
|
|
7096
7174
|
arg_types = {"this": True, "mode": False}
|
|
7097
7175
|
|
|
7098
7176
|
|
|
7177
|
+
class WeekStart(Expression):
|
|
7178
|
+
pass
|
|
7179
|
+
|
|
7180
|
+
|
|
7099
7181
|
class XMLElement(Func):
|
|
7100
7182
|
_sql_names = ["XMLELEMENT"]
|
|
7101
7183
|
arg_types = {"this": True, "expressions": False}
|
sqlglot/generator.py
CHANGED
|
@@ -219,6 +219,7 @@ class Generator(metaclass=_Generator):
|
|
|
219
219
|
exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
|
|
220
220
|
exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
|
|
221
221
|
exp.VolatileProperty: lambda *_: "VOLATILE",
|
|
222
|
+
exp.WeekStart: lambda self, e: f"WEEK({self.sql(e, 'this')})",
|
|
222
223
|
exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
|
|
223
224
|
exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
|
|
224
225
|
exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
|
|
@@ -691,6 +692,8 @@ class Generator(metaclass=_Generator):
|
|
|
691
692
|
|
|
692
693
|
RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: t.Tuple[t.Type[exp.Expression], ...] = ()
|
|
693
694
|
|
|
695
|
+
SAFE_JSON_PATH_KEY_RE = exp.SAFE_IDENTIFIER_RE
|
|
696
|
+
|
|
694
697
|
SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
|
|
695
698
|
|
|
696
699
|
__slots__ = (
|
|
@@ -2405,6 +2408,14 @@ class Generator(metaclass=_Generator):
|
|
|
2405
2408
|
tag = " TAG" if expression.args.get("tag") else ""
|
|
2406
2409
|
return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
|
|
2407
2410
|
|
|
2411
|
+
def queryband_sql(self, expression: exp.QueryBand) -> str:
|
|
2412
|
+
this = self.sql(expression, "this")
|
|
2413
|
+
update = " UPDATE" if expression.args.get("update") else ""
|
|
2414
|
+
scope = self.sql(expression, "scope")
|
|
2415
|
+
scope = f" FOR {scope}" if scope else ""
|
|
2416
|
+
|
|
2417
|
+
return f"QUERY_BAND = {this}{update}{scope}"
|
|
2418
|
+
|
|
2408
2419
|
def pragma_sql(self, expression: exp.Pragma) -> str:
|
|
2409
2420
|
return f"PRAGMA {self.sql(expression, 'this')}"
|
|
2410
2421
|
|
|
@@ -3479,14 +3490,15 @@ class Generator(metaclass=_Generator):
|
|
|
3479
3490
|
expressions = f"({expressions})" if expressions else ""
|
|
3480
3491
|
return f"ALTER{compound} SORTKEY {this or expressions}"
|
|
3481
3492
|
|
|
3482
|
-
def alterrename_sql(self, expression: exp.AlterRename) -> str:
|
|
3493
|
+
def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
|
|
3483
3494
|
if not self.RENAME_TABLE_WITH_DB:
|
|
3484
3495
|
# Remove db from tables
|
|
3485
3496
|
expression = expression.transform(
|
|
3486
3497
|
lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
|
|
3487
3498
|
).assert_is(exp.AlterRename)
|
|
3488
3499
|
this = self.sql(expression, "this")
|
|
3489
|
-
|
|
3500
|
+
to_kw = " TO" if include_to else ""
|
|
3501
|
+
return f"RENAME{to_kw} {this}"
|
|
3490
3502
|
|
|
3491
3503
|
def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
|
|
3492
3504
|
exists = " IF EXISTS" if expression.args.get("exists") else ""
|
|
@@ -4352,7 +4364,7 @@ class Generator(metaclass=_Generator):
|
|
|
4352
4364
|
this = self.json_path_part(this)
|
|
4353
4365
|
return f".{this}" if this else ""
|
|
4354
4366
|
|
|
4355
|
-
if
|
|
4367
|
+
if self.SAFE_JSON_PATH_KEY_RE.match(this):
|
|
4356
4368
|
return f".{this}"
|
|
4357
4369
|
|
|
4358
4370
|
this = self.json_path_part(this)
|
|
@@ -5127,3 +5139,15 @@ class Generator(metaclass=_Generator):
|
|
|
5127
5139
|
return self.sql(exp.Bracket(this=this, expressions=[expr]))
|
|
5128
5140
|
|
|
5129
5141
|
return self.sql(exp.JSONExtract(this=this, expression=self.dialect.to_json_path(expr)))
|
|
5142
|
+
|
|
5143
|
+
def datefromunixdate_sql(self, expression: exp.DateFromUnixDate) -> str:
|
|
5144
|
+
return self.sql(
|
|
5145
|
+
exp.DateAdd(
|
|
5146
|
+
this=exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
|
|
5147
|
+
expression=expression.this,
|
|
5148
|
+
unit=exp.var("DAY"),
|
|
5149
|
+
)
|
|
5150
|
+
)
|
|
5151
|
+
|
|
5152
|
+
def space_sql(self: Generator, expression: exp.Space) -> str:
|
|
5153
|
+
return self.sql(exp.Repeat(this=exp.Literal.string(" "), times=expression.this))
|
sqlglot/jsonpath.py
CHANGED
|
@@ -36,6 +36,10 @@ class JSONPathTokenizer(Tokenizer):
|
|
|
36
36
|
IDENTIFIER_ESCAPES = ["\\"]
|
|
37
37
|
STRING_ESCAPES = ["\\"]
|
|
38
38
|
|
|
39
|
+
VAR_TOKENS = {
|
|
40
|
+
TokenType.VAR,
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
|
|
40
44
|
def parse(path: str, dialect: DialectType = None) -> exp.JSONPath:
|
|
41
45
|
"""Takes in a JSON path string and parses it into a JSONPath expression."""
|
|
@@ -76,6 +80,9 @@ def parse(path: str, dialect: DialectType = None) -> exp.JSONPath:
|
|
|
76
80
|
raise ParseError(_error(f"Expected {token_type}"))
|
|
77
81
|
return None
|
|
78
82
|
|
|
83
|
+
def _match_set(types: t.Collection[TokenType]) -> t.Optional[Token]:
|
|
84
|
+
return _advance() if _curr() in types else None
|
|
85
|
+
|
|
79
86
|
def _parse_literal() -> t.Any:
|
|
80
87
|
token = _match(TokenType.STRING) or _match(TokenType.IDENTIFIER)
|
|
81
88
|
if token:
|
|
@@ -155,7 +162,7 @@ def parse(path: str, dialect: DialectType = None) -> exp.JSONPath:
|
|
|
155
162
|
"""
|
|
156
163
|
prev_index = i - 2
|
|
157
164
|
|
|
158
|
-
while
|
|
165
|
+
while _match_set(jsonpath_tokenizer.VAR_TOKENS):
|
|
159
166
|
pass
|
|
160
167
|
|
|
161
168
|
start = 0 if prev_index < 0 else tokens[prev_index].end + 1
|
|
@@ -177,7 +184,7 @@ def parse(path: str, dialect: DialectType = None) -> exp.JSONPath:
|
|
|
177
184
|
if _match(TokenType.DOT) or _match(TokenType.COLON):
|
|
178
185
|
recursive = _prev().text == ".."
|
|
179
186
|
|
|
180
|
-
if
|
|
187
|
+
if _match_set(jsonpath_tokenizer.VAR_TOKENS):
|
|
181
188
|
value: t.Optional[str | exp.JSONPathWildcard] = _parse_var_text()
|
|
182
189
|
elif _match(TokenType.IDENTIFIER):
|
|
183
190
|
value = _prev().text
|
|
@@ -194,7 +201,7 @@ def parse(path: str, dialect: DialectType = None) -> exp.JSONPath:
|
|
|
194
201
|
raise ParseError(_error("Expected key name or * after DOT"))
|
|
195
202
|
elif _match(TokenType.L_BRACKET):
|
|
196
203
|
expressions.append(_parse_bracket())
|
|
197
|
-
elif
|
|
204
|
+
elif _match_set(jsonpath_tokenizer.VAR_TOKENS):
|
|
198
205
|
expressions.append(exp.JSONPathKey(this=_parse_var_text()))
|
|
199
206
|
elif _match(TokenType.IDENTIFIER):
|
|
200
207
|
expressions.append(exp.JSONPathKey(this=_prev().text))
|
|
@@ -326,7 +326,10 @@ class TypeAnnotator(metaclass=_TypeAnnotator):
|
|
|
326
326
|
struct_type = exp.DataType(
|
|
327
327
|
this=exp.DataType.Type.STRUCT,
|
|
328
328
|
expressions=[
|
|
329
|
-
exp.ColumnDef(
|
|
329
|
+
exp.ColumnDef(
|
|
330
|
+
this=exp.to_identifier(select.output_name),
|
|
331
|
+
kind=select.type.copy() if select.type else None,
|
|
332
|
+
)
|
|
330
333
|
for select in scope.expression.selects
|
|
331
334
|
],
|
|
332
335
|
nested=True,
|
|
@@ -330,6 +330,10 @@ def _merge_expressions(outer_scope: Scope, inner_scope: Scope, alias: str) -> No
|
|
|
330
330
|
if isinstance(column.parent, (exp.Unary, exp.Binary)) and must_wrap_expression:
|
|
331
331
|
expression = exp.paren(expression, copy=False)
|
|
332
332
|
|
|
333
|
+
# make sure we do not accidentally change the name of the column
|
|
334
|
+
if isinstance(column.parent, exp.Select) and column.name != expression.name:
|
|
335
|
+
expression = exp.alias_(expression, column.name)
|
|
336
|
+
|
|
333
337
|
column.replace(expression.copy())
|
|
334
338
|
|
|
335
339
|
|
|
@@ -128,14 +128,6 @@ def qualify_tables(
|
|
|
128
128
|
table_alias = udtf.args.get("alias") or exp.TableAlias(
|
|
129
129
|
this=exp.to_identifier(next_alias_name())
|
|
130
130
|
)
|
|
131
|
-
if (
|
|
132
|
-
isinstance(udtf, exp.Unnest)
|
|
133
|
-
and dialect.UNNEST_COLUMN_ONLY
|
|
134
|
-
and not table_alias.columns
|
|
135
|
-
):
|
|
136
|
-
table_alias.set("columns", [table_alias.this.copy()])
|
|
137
|
-
table_alias.set("column_only", True)
|
|
138
|
-
|
|
139
131
|
udtf.set("alias", table_alias)
|
|
140
132
|
|
|
141
133
|
if not table_alias.name:
|
sqlglot/parser.py
CHANGED
|
@@ -577,7 +577,6 @@ class Parser(metaclass=_Parser):
|
|
|
577
577
|
|
|
578
578
|
TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - {
|
|
579
579
|
TokenType.ANTI,
|
|
580
|
-
TokenType.APPLY,
|
|
581
580
|
TokenType.ASOF,
|
|
582
581
|
TokenType.FULL,
|
|
583
582
|
TokenType.LEFT,
|
|
@@ -2084,7 +2083,24 @@ class Parser(metaclass=_Parser):
|
|
|
2084
2083
|
|
|
2085
2084
|
if create_token.token_type == TokenType.SEQUENCE:
|
|
2086
2085
|
expression = self._parse_types()
|
|
2087
|
-
|
|
2086
|
+
props = self._parse_properties()
|
|
2087
|
+
if props:
|
|
2088
|
+
sequence_props = exp.SequenceProperties()
|
|
2089
|
+
options = []
|
|
2090
|
+
for prop in props:
|
|
2091
|
+
if isinstance(prop, exp.SequenceProperties):
|
|
2092
|
+
for arg, value in prop.args.items():
|
|
2093
|
+
if arg == "options":
|
|
2094
|
+
options.extend(value)
|
|
2095
|
+
else:
|
|
2096
|
+
sequence_props.set(arg, value)
|
|
2097
|
+
prop.pop()
|
|
2098
|
+
|
|
2099
|
+
if options:
|
|
2100
|
+
sequence_props.set("options", options)
|
|
2101
|
+
|
|
2102
|
+
props.append("expressions", sequence_props)
|
|
2103
|
+
extend_props(props)
|
|
2088
2104
|
else:
|
|
2089
2105
|
expression = self._parse_ddl_select()
|
|
2090
2106
|
|
|
@@ -2222,11 +2238,17 @@ class Parser(metaclass=_Parser):
|
|
|
2222
2238
|
return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
|
|
2223
2239
|
|
|
2224
2240
|
index = self._index
|
|
2241
|
+
|
|
2242
|
+
seq_props = self._parse_sequence_properties()
|
|
2243
|
+
if seq_props:
|
|
2244
|
+
return seq_props
|
|
2245
|
+
|
|
2246
|
+
self._retreat(index)
|
|
2225
2247
|
key = self._parse_column()
|
|
2226
2248
|
|
|
2227
2249
|
if not self._match(TokenType.EQ):
|
|
2228
2250
|
self._retreat(index)
|
|
2229
|
-
return
|
|
2251
|
+
return None
|
|
2230
2252
|
|
|
2231
2253
|
# Transform the key to exp.Dot if it's dotted identifiers wrapped in exp.Column or to exp.Var otherwise
|
|
2232
2254
|
if isinstance(key, exp.Column):
|
|
@@ -3820,7 +3842,8 @@ class Parser(metaclass=_Parser):
|
|
|
3820
3842
|
elif self._match(TokenType.USING):
|
|
3821
3843
|
kwargs["using"] = self._parse_using_identifiers()
|
|
3822
3844
|
elif (
|
|
3823
|
-
not
|
|
3845
|
+
not method
|
|
3846
|
+
and not (outer_apply or cross_apply)
|
|
3824
3847
|
and not isinstance(kwargs["this"], exp.Unnest)
|
|
3825
3848
|
and not (kind and kind.token_type in (TokenType.CROSS, TokenType.ARRAY))
|
|
3826
3849
|
):
|
|
@@ -5255,7 +5278,7 @@ class Parser(metaclass=_Parser):
|
|
|
5255
5278
|
while self._match(TokenType.DOT):
|
|
5256
5279
|
type_name = f"{type_name}.{self._advance_any() and self._prev.text}"
|
|
5257
5280
|
|
|
5258
|
-
return exp.DataType.build(type_name, udt=True)
|
|
5281
|
+
return exp.DataType.build(type_name, dialect=self.dialect, udt=True)
|
|
5259
5282
|
|
|
5260
5283
|
def _parse_types(
|
|
5261
5284
|
self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
|
|
@@ -6556,7 +6579,7 @@ class Parser(metaclass=_Parser):
|
|
|
6556
6579
|
elif not to:
|
|
6557
6580
|
self.raise_error("Expected TYPE after CAST")
|
|
6558
6581
|
elif isinstance(to, exp.Identifier):
|
|
6559
|
-
to = exp.DataType.build(to.name, udt=True)
|
|
6582
|
+
to = exp.DataType.build(to.name, dialect=self.dialect, udt=True)
|
|
6560
6583
|
elif to.this == exp.DataType.Type.CHAR:
|
|
6561
6584
|
if self._match(TokenType.CHARACTER_SET):
|
|
6562
6585
|
to = self.expression(exp.CharacterSet, this=self._parse_var_or_string())
|
|
@@ -8684,3 +8707,18 @@ class Parser(metaclass=_Parser):
|
|
|
8684
8707
|
kwargs["requires_string"] = self.dialect.TRY_CAST_REQUIRES_STRING
|
|
8685
8708
|
|
|
8686
8709
|
return self.expression(exp_class, **kwargs)
|
|
8710
|
+
|
|
8711
|
+
def _parse_json_value(self) -> exp.JSONValue:
|
|
8712
|
+
this = self._parse_bitwise()
|
|
8713
|
+
self._match(TokenType.COMMA)
|
|
8714
|
+
path = self._parse_bitwise()
|
|
8715
|
+
|
|
8716
|
+
returning = self._match(TokenType.RETURNING) and self._parse_type()
|
|
8717
|
+
|
|
8718
|
+
return self.expression(
|
|
8719
|
+
exp.JSONValue,
|
|
8720
|
+
this=this,
|
|
8721
|
+
path=self.dialect.to_json_path(path),
|
|
8722
|
+
returning=returning,
|
|
8723
|
+
on_condition=self._parse_on_condition(),
|
|
8724
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlglot
|
|
3
|
-
Version: 27.
|
|
3
|
+
Version: 27.8.0
|
|
4
4
|
Summary: An easily customizable SQL parser and transpiler
|
|
5
5
|
Author-email: Toby Mao <toby.mao@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://sqlglot.com/
|
|
@@ -256,7 +256,7 @@ sqlglot.errors.ParseError: Expecting ). Line 1, Col: 34.
|
|
|
256
256
|
Structured syntax errors are accessible for programmatic use:
|
|
257
257
|
|
|
258
258
|
```python
|
|
259
|
-
import sqlglot
|
|
259
|
+
import sqlglot.errors
|
|
260
260
|
try:
|
|
261
261
|
sqlglot.transpile("SELECT foo FROM (SELECT baz FROM t")
|
|
262
262
|
except sqlglot.errors.ParseError as e:
|