sqlglot 28.4.0__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.0.dist-info → sqlglot-28.8.0.dist-info}/METADATA +47 -2
- sqlglot-28.8.0.dist-info/RECORD +95 -0
- {sqlglot-28.4.0.dist-info → sqlglot-28.8.0.dist-info}/WHEEL +1 -1
- sqlglot-28.4.0.dist-info/RECORD +0 -92
- {sqlglot-28.4.0.dist-info → sqlglot-28.8.0.dist-info}/licenses/LICENSE +0 -0
- {sqlglot-28.4.0.dist-info → sqlglot-28.8.0.dist-info}/top_level.txt +0 -0
sqlglot/dialects/exasol.py
CHANGED
|
@@ -16,6 +16,7 @@ from sqlglot.dialects.dialect import (
|
|
|
16
16
|
build_date_delta,
|
|
17
17
|
no_last_day_sql,
|
|
18
18
|
DATE_ADD_OR_SUB,
|
|
19
|
+
build_timetostr_or_tochar,
|
|
19
20
|
)
|
|
20
21
|
from sqlglot.generator import unsupported_args
|
|
21
22
|
from sqlglot.helper import seq_get
|
|
@@ -319,6 +320,7 @@ class Exasol(Dialect):
|
|
|
319
320
|
"ENDIF": TokenType.END,
|
|
320
321
|
"LONG VARCHAR": TokenType.TEXT,
|
|
321
322
|
"SEPARATOR": TokenType.SEPARATOR,
|
|
323
|
+
"SYSTIMESTAMP": TokenType.SYSTIMESTAMP,
|
|
322
324
|
}
|
|
323
325
|
KEYWORDS.pop("DIV")
|
|
324
326
|
|
|
@@ -368,7 +370,7 @@ class Exasol(Dialect):
|
|
|
368
370
|
"TRUNCATE": _build_trunc,
|
|
369
371
|
"VAR_POP": exp.VariancePop.from_arg_list,
|
|
370
372
|
"APPROXIMATE_COUNT_DISTINCT": exp.ApproxDistinct.from_arg_list,
|
|
371
|
-
"TO_CHAR":
|
|
373
|
+
"TO_CHAR": build_timetostr_or_tochar,
|
|
372
374
|
"TO_DATE": build_formatted_time(exp.TsOrDsToDate, "exasol"),
|
|
373
375
|
# https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/convert_tz.htm
|
|
374
376
|
"CONVERT_TZ": lambda args: exp.ConvertTimezone(
|
|
@@ -387,6 +389,17 @@ class Exasol(Dialect):
|
|
|
387
389
|
this=self._match(TokenType.IS) and self._parse_string(),
|
|
388
390
|
),
|
|
389
391
|
}
|
|
392
|
+
|
|
393
|
+
FUNC_TOKENS = {
|
|
394
|
+
*parser.Parser.FUNC_TOKENS,
|
|
395
|
+
TokenType.SYSTIMESTAMP,
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
NO_PAREN_FUNCTIONS = {
|
|
399
|
+
**parser.Parser.NO_PAREN_FUNCTIONS,
|
|
400
|
+
TokenType.SYSTIMESTAMP: exp.Systimestamp,
|
|
401
|
+
}
|
|
402
|
+
|
|
390
403
|
FUNCTION_PARSERS = {
|
|
391
404
|
**parser.Parser.FUNCTION_PARSERS,
|
|
392
405
|
# https://docs.exasol.com/db/latest/sql_references/functions/alphabeticallistfunctions/listagg.htm
|
|
@@ -438,6 +451,9 @@ class Exasol(Dialect):
|
|
|
438
451
|
exp.DataType.Type.DECIMAL128: "DECIMAL",
|
|
439
452
|
exp.DataType.Type.DECIMAL256: "DECIMAL",
|
|
440
453
|
exp.DataType.Type.DATETIME: "TIMESTAMP",
|
|
454
|
+
exp.DataType.Type.TIMESTAMPTZ: "TIMESTAMP",
|
|
455
|
+
exp.DataType.Type.TIMESTAMPLTZ: "TIMESTAMP",
|
|
456
|
+
exp.DataType.Type.TIMESTAMPNTZ: "TIMESTAMP",
|
|
441
457
|
}
|
|
442
458
|
|
|
443
459
|
def datatype_sql(self, expression: exp.DataType) -> str:
|
sqlglot/dialects/hive.py
CHANGED
|
@@ -30,6 +30,7 @@ from sqlglot.dialects.dialect import (
|
|
|
30
30
|
struct_extract_sql,
|
|
31
31
|
time_format,
|
|
32
32
|
timestrtotime_sql,
|
|
33
|
+
trim_sql,
|
|
33
34
|
unit_to_str,
|
|
34
35
|
var_map_sql,
|
|
35
36
|
sequence_sql,
|
|
@@ -48,6 +49,10 @@ from sqlglot.generator import unsupported_args
|
|
|
48
49
|
from sqlglot.optimizer.annotate_types import TypeAnnotator
|
|
49
50
|
from sqlglot.typing.hive import EXPRESSION_METADATA
|
|
50
51
|
|
|
52
|
+
if t.TYPE_CHECKING:
|
|
53
|
+
from sqlglot._typing import F
|
|
54
|
+
|
|
55
|
+
|
|
51
56
|
# (FuncType, Multiplier)
|
|
52
57
|
DATE_DELTA_INTERVAL = {
|
|
53
58
|
"YEAR": ("ADD_MONTHS", 12),
|
|
@@ -315,6 +320,12 @@ class Hive(Dialect):
|
|
|
315
320
|
CHANGE_COLUMN_ALTER_SYNTAX = False
|
|
316
321
|
# Whether the dialect supports using ALTER COLUMN syntax with CHANGE COLUMN.
|
|
317
322
|
|
|
323
|
+
FUNCTION_PARSERS = {
|
|
324
|
+
**parser.Parser.FUNCTION_PARSERS,
|
|
325
|
+
"PERCENTILE": lambda self: self._parse_quantile_function(exp.Quantile),
|
|
326
|
+
"PERCENTILE_APPROX": lambda self: self._parse_quantile_function(exp.ApproxQuantile),
|
|
327
|
+
}
|
|
328
|
+
|
|
318
329
|
FUNCTIONS = {
|
|
319
330
|
**parser.Parser.FUNCTIONS,
|
|
320
331
|
"BASE64": exp.ToBase64.from_arg_list,
|
|
@@ -345,8 +356,6 @@ class Hive(Dialect):
|
|
|
345
356
|
"LAST_VALUE": _build_with_ignore_nulls(exp.LastValue),
|
|
346
357
|
"MAP": parser.build_var_map,
|
|
347
358
|
"MONTH": lambda args: exp.Month(this=exp.TsOrDsToDate.from_arg_list(args)),
|
|
348
|
-
"PERCENTILE": exp.Quantile.from_arg_list,
|
|
349
|
-
"PERCENTILE_APPROX": exp.ApproxQuantile.from_arg_list,
|
|
350
359
|
"REGEXP_EXTRACT": build_regexp_extract(exp.RegexpExtract),
|
|
351
360
|
"REGEXP_EXTRACT_ALL": build_regexp_extract(exp.RegexpExtractAll),
|
|
352
361
|
"SEQUENCE": exp.GenerateSeries.from_arg_list,
|
|
@@ -423,6 +432,21 @@ class Hive(Dialect):
|
|
|
423
432
|
record_reader=record_reader,
|
|
424
433
|
)
|
|
425
434
|
|
|
435
|
+
def _parse_quantile_function(self, func: t.Type[F]) -> F:
|
|
436
|
+
if self._match(TokenType.DISTINCT):
|
|
437
|
+
first_arg: t.Optional[exp.Expression] = self.expression(
|
|
438
|
+
exp.Distinct, expressions=[self._parse_lambda()]
|
|
439
|
+
)
|
|
440
|
+
else:
|
|
441
|
+
self._match(TokenType.ALL)
|
|
442
|
+
first_arg = self._parse_lambda()
|
|
443
|
+
|
|
444
|
+
args = [first_arg]
|
|
445
|
+
if self._match(TokenType.COMMA):
|
|
446
|
+
args.extend(self._parse_function_args())
|
|
447
|
+
|
|
448
|
+
return func.from_arg_list(args)
|
|
449
|
+
|
|
426
450
|
def _parse_types(
|
|
427
451
|
self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
|
|
428
452
|
) -> t.Optional[exp.Expression]:
|
|
@@ -660,6 +684,7 @@ class Hive(Dialect):
|
|
|
660
684
|
exp.TsOrDsDiff: _date_diff_sql,
|
|
661
685
|
exp.TsOrDsToDate: _to_date_sql,
|
|
662
686
|
exp.TryCast: no_trycast_sql,
|
|
687
|
+
exp.Trim: trim_sql,
|
|
663
688
|
exp.Unicode: rename_func("ASCII"),
|
|
664
689
|
exp.UnixToStr: lambda self, e: self.func(
|
|
665
690
|
"FROM_UNIXTIME", e.this, time_format("hive")(self, e)
|
sqlglot/dialects/mysql.py
CHANGED
|
@@ -30,6 +30,7 @@ from sqlglot.dialects.dialect import (
|
|
|
30
30
|
from sqlglot.generator import unsupported_args
|
|
31
31
|
from sqlglot.helper import seq_get
|
|
32
32
|
from sqlglot.tokens import TokenType
|
|
33
|
+
from sqlglot.typing.mysql import EXPRESSION_METADATA
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
def _show_parser(*args: t.Any, **kwargs: t.Any) -> t.Callable[[MySQL.Parser], exp.Show]:
|
|
@@ -164,6 +165,9 @@ class MySQL(Dialect):
|
|
|
164
165
|
SUPPORTS_SEMI_ANTI_JOIN = False
|
|
165
166
|
SAFE_DIVISION = True
|
|
166
167
|
SAFE_TO_ELIMINATE_DOUBLE_NEGATION = False
|
|
168
|
+
LEAST_GREATEST_IGNORES_NULLS = False
|
|
169
|
+
|
|
170
|
+
EXPRESSION_METADATA = EXPRESSION_METADATA.copy()
|
|
167
171
|
|
|
168
172
|
# https://prestodb.io/docs/current/functions/datetime.html#mysql-date-functions
|
|
169
173
|
TIME_MAPPING = {
|
|
@@ -292,6 +296,7 @@ class MySQL(Dialect):
|
|
|
292
296
|
TokenType.MOD,
|
|
293
297
|
TokenType.SCHEMA,
|
|
294
298
|
TokenType.VALUES,
|
|
299
|
+
TokenType.CHARACTER_SET,
|
|
295
300
|
}
|
|
296
301
|
|
|
297
302
|
CONJUNCTION = {
|
|
@@ -372,11 +377,6 @@ class MySQL(Dialect):
|
|
|
372
377
|
|
|
373
378
|
FUNCTION_PARSERS = {
|
|
374
379
|
**parser.Parser.FUNCTION_PARSERS,
|
|
375
|
-
"CHAR": lambda self: self.expression(
|
|
376
|
-
exp.Chr,
|
|
377
|
-
expressions=self._parse_csv(self._parse_assignment),
|
|
378
|
-
charset=self._match(TokenType.USING) and self._parse_var(),
|
|
379
|
-
),
|
|
380
380
|
"GROUP_CONCAT": lambda self: self._parse_group_concat(),
|
|
381
381
|
# https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values
|
|
382
382
|
"VALUES": lambda self: self.expression(
|
|
@@ -449,6 +449,7 @@ class MySQL(Dialect):
|
|
|
449
449
|
PROPERTY_PARSERS = {
|
|
450
450
|
**parser.Parser.PROPERTY_PARSERS,
|
|
451
451
|
"LOCK": lambda self: self._parse_property_assignment(exp.LockProperty),
|
|
452
|
+
"PARTITION BY": lambda self: self._parse_partition_property(),
|
|
452
453
|
}
|
|
453
454
|
|
|
454
455
|
SET_PARSERS = {
|
|
@@ -732,6 +733,63 @@ class MySQL(Dialect):
|
|
|
732
733
|
|
|
733
734
|
return self.expression(exp.AlterIndex, this=index, visible=visible)
|
|
734
735
|
|
|
736
|
+
def _parse_partition_property(
|
|
737
|
+
self,
|
|
738
|
+
) -> t.Optional[exp.Expression] | t.List[exp.Expression]:
|
|
739
|
+
partition_cls: t.Optional[t.Type[exp.Expression]] = None
|
|
740
|
+
value_parser = None
|
|
741
|
+
|
|
742
|
+
if self._match_text_seq("RANGE"):
|
|
743
|
+
partition_cls = exp.PartitionByRangeProperty
|
|
744
|
+
value_parser = self._parse_partition_range_value
|
|
745
|
+
elif self._match_text_seq("LIST"):
|
|
746
|
+
partition_cls = exp.PartitionByListProperty
|
|
747
|
+
value_parser = self._parse_partition_list_value
|
|
748
|
+
|
|
749
|
+
if not partition_cls or not value_parser:
|
|
750
|
+
return None
|
|
751
|
+
|
|
752
|
+
partition_expressions = self._parse_wrapped_csv(self._parse_assignment)
|
|
753
|
+
|
|
754
|
+
# For Doris and Starrocks
|
|
755
|
+
if not self._match_text_seq("(", "PARTITION", advance=False):
|
|
756
|
+
return partition_expressions
|
|
757
|
+
|
|
758
|
+
create_expressions = self._parse_wrapped_csv(value_parser)
|
|
759
|
+
|
|
760
|
+
return self.expression(
|
|
761
|
+
partition_cls,
|
|
762
|
+
partition_expressions=partition_expressions,
|
|
763
|
+
create_expressions=create_expressions,
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
def _parse_partition_range_value(self) -> t.Optional[exp.Expression]:
|
|
767
|
+
self._match_text_seq("PARTITION")
|
|
768
|
+
name = self._parse_id_var()
|
|
769
|
+
|
|
770
|
+
if not self._match_text_seq("VALUES", "LESS", "THAN"):
|
|
771
|
+
return name
|
|
772
|
+
|
|
773
|
+
values = self._parse_wrapped_csv(self._parse_expression)
|
|
774
|
+
|
|
775
|
+
if (
|
|
776
|
+
len(values) == 1
|
|
777
|
+
and isinstance(values[0], exp.Column)
|
|
778
|
+
and values[0].name.upper() == "MAXVALUE"
|
|
779
|
+
):
|
|
780
|
+
values = [exp.var("MAXVALUE")]
|
|
781
|
+
|
|
782
|
+
part_range = self.expression(exp.PartitionRange, this=name, expressions=values)
|
|
783
|
+
return self.expression(exp.Partition, expressions=[part_range])
|
|
784
|
+
|
|
785
|
+
def _parse_partition_list_value(self) -> exp.Partition:
|
|
786
|
+
self._match_text_seq("PARTITION")
|
|
787
|
+
name = self._parse_id_var()
|
|
788
|
+
self._match_text_seq("VALUES", "IN")
|
|
789
|
+
values = self._parse_wrapped_csv(self._parse_expression)
|
|
790
|
+
part_list = self.expression(exp.PartitionList, this=name, expressions=values)
|
|
791
|
+
return self.expression(exp.Partition, expressions=[part_list])
|
|
792
|
+
|
|
735
793
|
class Generator(generator.Generator):
|
|
736
794
|
INTERVAL_ALLOWS_PLURAL_FORM = False
|
|
737
795
|
LOCKING_READS_SUPPORTED = True
|
|
@@ -752,6 +810,7 @@ class MySQL(Dialect):
|
|
|
752
810
|
WRAP_DERIVED_VALUES = False
|
|
753
811
|
VARCHAR_REQUIRES_SIZE = True
|
|
754
812
|
SUPPORTS_MEDIAN = False
|
|
813
|
+
UPDATE_STATEMENT_SUPPORTS_FROM = False
|
|
755
814
|
|
|
756
815
|
TRANSFORMS = {
|
|
757
816
|
**generator.Generator.TRANSFORMS,
|
|
@@ -760,6 +819,7 @@ class MySQL(Dialect):
|
|
|
760
819
|
exp.BitwiseOrAgg: rename_func("BIT_OR"),
|
|
761
820
|
exp.BitwiseXorAgg: rename_func("BIT_XOR"),
|
|
762
821
|
exp.BitwiseCount: rename_func("BIT_COUNT"),
|
|
822
|
+
exp.Chr: lambda self, e: self.chr_sql(e, "CHAR"),
|
|
763
823
|
exp.CurrentDate: no_paren_current_date_sql,
|
|
764
824
|
exp.DateDiff: _remove_ts_or_ds_to_date(
|
|
765
825
|
lambda self, e: self.func("DATEDIFF", e.this, e.expression), ("this", "expression")
|
|
@@ -869,6 +929,9 @@ class MySQL(Dialect):
|
|
|
869
929
|
**generator.Generator.PROPERTIES_LOCATION,
|
|
870
930
|
exp.TransientProperty: exp.Properties.Location.UNSUPPORTED,
|
|
871
931
|
exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED,
|
|
932
|
+
exp.PartitionedByProperty: exp.Properties.Location.UNSUPPORTED,
|
|
933
|
+
exp.PartitionByRangeProperty: exp.Properties.Location.POST_SCHEMA,
|
|
934
|
+
exp.PartitionByListProperty: exp.Properties.Location.POST_SCHEMA,
|
|
872
935
|
}
|
|
873
936
|
|
|
874
937
|
LIMIT_FETCH = "LIMIT"
|
|
@@ -1281,6 +1344,12 @@ class MySQL(Dialect):
|
|
|
1281
1344
|
|
|
1282
1345
|
return f"SHOW{full}{global_}{this}{json}{target}{for_table}{types}{db}{query}{log}{position}{channel}{mutex_or_status}{like}{where}{offset}{limit}{for_group}{for_user}{for_role}{into_outfile}"
|
|
1283
1346
|
|
|
1347
|
+
def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
|
|
1348
|
+
"""To avoid TO keyword in ALTER ... RENAME statements.
|
|
1349
|
+
It's moved from Doris, because it's the same for all MySQL, Doris, and StarRocks.
|
|
1350
|
+
"""
|
|
1351
|
+
return super().alterrename_sql(expression, include_to=False)
|
|
1352
|
+
|
|
1284
1353
|
def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
|
|
1285
1354
|
dtype = self.sql(expression, "dtype")
|
|
1286
1355
|
if not dtype:
|
|
@@ -1301,12 +1370,6 @@ class MySQL(Dialect):
|
|
|
1301
1370
|
return f" LIMIT {limit_offset}"
|
|
1302
1371
|
return ""
|
|
1303
1372
|
|
|
1304
|
-
def chr_sql(self, expression: exp.Chr) -> str:
|
|
1305
|
-
this = self.expressions(sqls=[expression.this] + expression.expressions)
|
|
1306
|
-
charset = expression.args.get("charset")
|
|
1307
|
-
using = f" USING {self.sql(charset)}" if charset else ""
|
|
1308
|
-
return f"CHAR({this}{using})"
|
|
1309
|
-
|
|
1310
1373
|
def timestamptrunc_sql(self, expression: exp.TimestampTrunc) -> str:
|
|
1311
1374
|
unit = expression.args.get("unit")
|
|
1312
1375
|
|
|
@@ -1342,3 +1405,32 @@ class MySQL(Dialect):
|
|
|
1342
1405
|
@unsupported_args("this")
|
|
1343
1406
|
def currentschema_sql(self, expression: exp.CurrentSchema) -> str:
|
|
1344
1407
|
return self.func("SCHEMA")
|
|
1408
|
+
|
|
1409
|
+
def partition_sql(self, expression: exp.Partition) -> str:
|
|
1410
|
+
parent = expression.parent
|
|
1411
|
+
if isinstance(parent, (exp.PartitionByRangeProperty, exp.PartitionByListProperty)):
|
|
1412
|
+
return self.expressions(expression, flat=True)
|
|
1413
|
+
return super().partition_sql(expression)
|
|
1414
|
+
|
|
1415
|
+
def _partition_by_sql(
|
|
1416
|
+
self, expression: exp.PartitionByRangeProperty | exp.PartitionByListProperty, kind: str
|
|
1417
|
+
) -> str:
|
|
1418
|
+
partitions = self.expressions(expression, key="partition_expressions", flat=True)
|
|
1419
|
+
create = self.expressions(expression, key="create_expressions", flat=True)
|
|
1420
|
+
return f"PARTITION BY {kind} ({partitions}) ({create})"
|
|
1421
|
+
|
|
1422
|
+
def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
|
|
1423
|
+
return self._partition_by_sql(expression, "RANGE")
|
|
1424
|
+
|
|
1425
|
+
def partitionbylistproperty_sql(self, expression: exp.PartitionByListProperty) -> str:
|
|
1426
|
+
return self._partition_by_sql(expression, "LIST")
|
|
1427
|
+
|
|
1428
|
+
def partitionlist_sql(self, expression: exp.PartitionList) -> str:
|
|
1429
|
+
name = self.sql(expression, "this")
|
|
1430
|
+
values = self.expressions(expression, flat=True)
|
|
1431
|
+
return f"PARTITION {name} VALUES IN ({values})"
|
|
1432
|
+
|
|
1433
|
+
def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
|
|
1434
|
+
name = self.sql(expression, "this")
|
|
1435
|
+
values = self.expressions(expression, flat=True)
|
|
1436
|
+
return f"PARTITION {name} VALUES LESS THAN ({values})"
|
sqlglot/dialects/oracle.py
CHANGED
|
@@ -108,6 +108,7 @@ class Oracle(Dialect):
|
|
|
108
108
|
"START": TokenType.BEGIN,
|
|
109
109
|
"TOP": TokenType.TOP,
|
|
110
110
|
"VARCHAR2": TokenType.VARCHAR,
|
|
111
|
+
"SYSTIMESTAMP": TokenType.SYSTIMESTAMP,
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
class Parser(parser.Parser):
|
|
@@ -139,6 +140,11 @@ class Oracle(Dialect):
|
|
|
139
140
|
"DBMS_RANDOM": lambda self: self._parse_dbms_random(),
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
NO_PAREN_FUNCTIONS = {
|
|
144
|
+
**parser.Parser.NO_PAREN_FUNCTIONS,
|
|
145
|
+
TokenType.SYSTIMESTAMP: exp.Systimestamp,
|
|
146
|
+
}
|
|
147
|
+
|
|
142
148
|
FUNCTION_PARSERS: t.Dict[str, t.Callable] = {
|
|
143
149
|
**parser.Parser.FUNCTION_PARSERS,
|
|
144
150
|
"JSON_ARRAY": lambda self: self._parse_json_array(
|
|
@@ -172,7 +178,11 @@ class Oracle(Dialect):
|
|
|
172
178
|
TYPE_LITERAL_PARSERS = {
|
|
173
179
|
exp.DataType.Type.DATE: lambda self, this, _: self.expression(
|
|
174
180
|
exp.DateStrToDate, this=this
|
|
175
|
-
)
|
|
181
|
+
),
|
|
182
|
+
# https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/NLS_TIMESTAMP_FORMAT.html
|
|
183
|
+
exp.DataType.Type.TIMESTAMP: lambda self, this, _: _build_to_timestamp(
|
|
184
|
+
[this, '"%Y-%m-%d %H:%M:%S.%f"']
|
|
185
|
+
),
|
|
176
186
|
}
|
|
177
187
|
|
|
178
188
|
# SELECT UNIQUE .. is old-style Oracle syntax for SELECT DISTINCT ..
|
|
@@ -275,6 +285,22 @@ class Oracle(Dialect):
|
|
|
275
285
|
def _parse_connect_with_prior(self):
|
|
276
286
|
return self._parse_assignment()
|
|
277
287
|
|
|
288
|
+
def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
|
|
289
|
+
this = super()._parse_column_ops(this)
|
|
290
|
+
|
|
291
|
+
if not this:
|
|
292
|
+
return this
|
|
293
|
+
|
|
294
|
+
index = self._index
|
|
295
|
+
|
|
296
|
+
# https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Interval-Expressions.html
|
|
297
|
+
interval_span = self._parse_interval_span(this)
|
|
298
|
+
if isinstance(interval_span.args.get("unit"), exp.IntervalSpan):
|
|
299
|
+
return interval_span
|
|
300
|
+
|
|
301
|
+
self._retreat(index)
|
|
302
|
+
return this
|
|
303
|
+
|
|
278
304
|
def _parse_insert_table(self) -> t.Optional[exp.Expression]:
|
|
279
305
|
# Oracle does not use AS for INSERT INTO alias
|
|
280
306
|
# https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/INSERT.html
|
|
@@ -368,6 +394,7 @@ class Oracle(Dialect):
|
|
|
368
394
|
e: f"TO_DATE('1970-01-01', 'YYYY-MM-DD') + ({self.sql(e, 'this')} / 86400)",
|
|
369
395
|
exp.UtcTimestamp: rename_func("UTC_TIMESTAMP"),
|
|
370
396
|
exp.UtcTime: rename_func("UTC_TIME"),
|
|
397
|
+
exp.Systimestamp: lambda self, e: "SYSTIMESTAMP",
|
|
371
398
|
}
|
|
372
399
|
|
|
373
400
|
PROPERTIES_LOCATION = {
|
|
@@ -420,3 +447,13 @@ class Oracle(Dialect):
|
|
|
420
447
|
|
|
421
448
|
def isascii_sql(self, expression: exp.IsAscii) -> str:
|
|
422
449
|
return f"NVL(REGEXP_LIKE({self.sql(expression.this)}, '^[' || CHR(1) || '-' || CHR(127) || ']*$'), TRUE)"
|
|
450
|
+
|
|
451
|
+
def interval_sql(self, expression: exp.Interval) -> str:
|
|
452
|
+
return f"{'INTERVAL ' if isinstance(expression.this, exp.Literal) else ''}{self.sql(expression, 'this')} {self.sql(expression, 'unit')}"
|
|
453
|
+
|
|
454
|
+
def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
|
|
455
|
+
param_constraint = expression.find(exp.InOutColumnConstraint)
|
|
456
|
+
if param_constraint:
|
|
457
|
+
sep = f" {self.sql(param_constraint)} "
|
|
458
|
+
param_constraint.pop()
|
|
459
|
+
return super().columndef_sql(expression, sep)
|