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/postgres.py
CHANGED
|
@@ -8,11 +8,14 @@ from sqlglot.dialects.dialect import (
|
|
|
8
8
|
Dialect,
|
|
9
9
|
JSON_EXTRACT_TYPE,
|
|
10
10
|
any_value_to_max_sql,
|
|
11
|
+
array_append_sql,
|
|
12
|
+
array_concat_sql,
|
|
11
13
|
binary_from_function,
|
|
12
14
|
bool_xor_sql,
|
|
13
15
|
datestrtodate_sql,
|
|
14
16
|
build_formatted_time,
|
|
15
17
|
filter_array_using_unnest,
|
|
18
|
+
getbit_sql,
|
|
16
19
|
inline_array_sql,
|
|
17
20
|
json_extract_segments,
|
|
18
21
|
json_path_key_only_name,
|
|
@@ -345,6 +348,7 @@ class Postgres(Dialect):
|
|
|
345
348
|
BIT_STRINGS = [("b'", "'"), ("B'", "'")]
|
|
346
349
|
HEX_STRINGS = [("x'", "'"), ("X'", "'")]
|
|
347
350
|
BYTE_STRINGS = [("e'", "'"), ("E'", "'")]
|
|
351
|
+
BYTE_STRING_ESCAPES = ["'", "\\"]
|
|
348
352
|
HEREDOC_STRINGS = ["$"]
|
|
349
353
|
|
|
350
354
|
HEREDOC_TAG_IS_IDENTIFIER = True
|
|
@@ -358,8 +362,6 @@ class Postgres(Dialect):
|
|
|
358
362
|
"<@": TokenType.LT_AT,
|
|
359
363
|
"?&": TokenType.QMARK_AMP,
|
|
360
364
|
"?|": TokenType.QMARK_PIPE,
|
|
361
|
-
"&<": TokenType.AMP_LT,
|
|
362
|
-
"&>": TokenType.AMP_GT,
|
|
363
365
|
"#-": TokenType.HASH_DASH,
|
|
364
366
|
"|/": TokenType.PIPE_SLASH,
|
|
365
367
|
"||/": TokenType.DPIPE_SLASH,
|
|
@@ -376,7 +378,7 @@ class Postgres(Dialect):
|
|
|
376
378
|
"NAME": TokenType.NAME,
|
|
377
379
|
"OID": TokenType.OBJECT_IDENTIFIER,
|
|
378
380
|
"ONLY": TokenType.ONLY,
|
|
379
|
-
"
|
|
381
|
+
"POINT": TokenType.POINT,
|
|
380
382
|
"REFRESH": TokenType.COMMAND,
|
|
381
383
|
"REINDEX": TokenType.COMMAND,
|
|
382
384
|
"RESET": TokenType.COMMAND,
|
|
@@ -396,6 +398,8 @@ class Postgres(Dialect):
|
|
|
396
398
|
"REGTYPE": TokenType.OBJECT_IDENTIFIER,
|
|
397
399
|
"FLOAT": TokenType.DOUBLE,
|
|
398
400
|
"XML": TokenType.XML,
|
|
401
|
+
"VARIADIC": TokenType.VARIADIC,
|
|
402
|
+
"INOUT": TokenType.INOUT,
|
|
399
403
|
}
|
|
400
404
|
KEYWORDS.pop("/*+")
|
|
401
405
|
KEYWORDS.pop("DIV")
|
|
@@ -424,6 +428,9 @@ class Postgres(Dialect):
|
|
|
424
428
|
|
|
425
429
|
FUNCTIONS = {
|
|
426
430
|
**parser.Parser.FUNCTIONS,
|
|
431
|
+
"ARRAY_PREPEND": lambda args: exp.ArrayPrepend(
|
|
432
|
+
this=seq_get(args, 1), expression=seq_get(args, 0)
|
|
433
|
+
),
|
|
427
434
|
"BIT_AND": exp.BitwiseAndAgg.from_arg_list,
|
|
428
435
|
"BIT_OR": exp.BitwiseOrAgg.from_arg_list,
|
|
429
436
|
"BIT_XOR": exp.BitwiseXorAgg.from_arg_list,
|
|
@@ -432,6 +439,9 @@ class Postgres(Dialect):
|
|
|
432
439
|
binary_from_function(exp.IntDiv)(args), exp.DataType.Type.DECIMAL
|
|
433
440
|
),
|
|
434
441
|
"GENERATE_SERIES": _build_generate_series,
|
|
442
|
+
"GET_BIT": lambda args: exp.Getbit(
|
|
443
|
+
this=seq_get(args, 0), expression=seq_get(args, 1), zero_is_msb=True
|
|
444
|
+
),
|
|
435
445
|
"JSON_EXTRACT_PATH": build_json_extract_path(exp.JSONExtract),
|
|
436
446
|
"JSON_EXTRACT_PATH_TEXT": build_json_extract_path(exp.JSONExtractScalar),
|
|
437
447
|
"LENGTH": lambda args: exp.Length(this=seq_get(args, 0), encoding=seq_get(args, 1)),
|
|
@@ -449,6 +459,16 @@ class Postgres(Dialect):
|
|
|
449
459
|
"LEVENSHTEIN_LESS_EQUAL": _build_levenshtein_less_equal,
|
|
450
460
|
"JSON_OBJECT_AGG": lambda args: exp.JSONObjectAgg(expressions=args),
|
|
451
461
|
"JSONB_OBJECT_AGG": exp.JSONBObjectAgg.from_arg_list,
|
|
462
|
+
"WIDTH_BUCKET": lambda args: exp.WidthBucket(
|
|
463
|
+
this=seq_get(args, 0), threshold=seq_get(args, 1)
|
|
464
|
+
)
|
|
465
|
+
if len(args) == 2
|
|
466
|
+
else exp.WidthBucket.from_arg_list(args),
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
NO_PAREN_FUNCTION_PARSERS = {
|
|
470
|
+
**parser.Parser.NO_PAREN_FUNCTION_PARSERS,
|
|
471
|
+
"VARIADIC": lambda self: self.expression(exp.Variadic, this=self._parse_bitwise()),
|
|
452
472
|
}
|
|
453
473
|
|
|
454
474
|
NO_PAREN_FUNCTIONS = {
|
|
@@ -479,12 +499,9 @@ class Postgres(Dialect):
|
|
|
479
499
|
RANGE_PARSERS = {
|
|
480
500
|
**parser.Parser.RANGE_PARSERS,
|
|
481
501
|
TokenType.DAMP: binary_range_parser(exp.ArrayOverlaps),
|
|
482
|
-
TokenType.AMP_LT: binary_range_parser(exp.ExtendsLeft),
|
|
483
|
-
TokenType.AMP_GT: binary_range_parser(exp.ExtendsRight),
|
|
484
502
|
TokenType.DAT: lambda self, this: self.expression(
|
|
485
503
|
exp.MatchAgainst, this=self._parse_bitwise(), expressions=[this]
|
|
486
504
|
),
|
|
487
|
-
TokenType.OPERATOR: lambda self, this: self._parse_operator(this),
|
|
488
505
|
}
|
|
489
506
|
|
|
490
507
|
STATEMENT_PARSERS = {
|
|
@@ -492,6 +509,12 @@ class Postgres(Dialect):
|
|
|
492
509
|
TokenType.END: lambda self: self._parse_commit_or_rollback(),
|
|
493
510
|
}
|
|
494
511
|
|
|
512
|
+
UNARY_PARSERS = {
|
|
513
|
+
**parser.Parser.UNARY_PARSERS,
|
|
514
|
+
# The `~` token is remapped from TILDE to RLIKE in Postgres due to the binary REGEXP LIKE operator
|
|
515
|
+
TokenType.RLIKE: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()),
|
|
516
|
+
}
|
|
517
|
+
|
|
495
518
|
JSON_ARROWS_REQUIRE_JSON_TYPE = True
|
|
496
519
|
|
|
497
520
|
COLUMN_OPERATORS = {
|
|
@@ -508,6 +531,88 @@ class Postgres(Dialect):
|
|
|
508
531
|
),
|
|
509
532
|
}
|
|
510
533
|
|
|
534
|
+
ARG_MODE_TOKENS = {TokenType.IN, TokenType.OUT, TokenType.INOUT, TokenType.VARIADIC}
|
|
535
|
+
|
|
536
|
+
def _parse_parameter_mode(self) -> t.Optional[TokenType]:
|
|
537
|
+
"""
|
|
538
|
+
Parse PostgreSQL function parameter mode (IN, OUT, INOUT, VARIADIC).
|
|
539
|
+
|
|
540
|
+
Disambiguates between mode keywords and identifiers with the same name:
|
|
541
|
+
- MODE TYPE → keyword is identifier (e.g., "out INT")
|
|
542
|
+
- MODE NAME TYPE → keyword is mode (e.g., "OUT x INT")
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
Mode token type if current token is a mode keyword, None otherwise.
|
|
546
|
+
"""
|
|
547
|
+
if not self._match_set(self.ARG_MODE_TOKENS, advance=False) or not self._next:
|
|
548
|
+
return None
|
|
549
|
+
|
|
550
|
+
mode_token = self._curr
|
|
551
|
+
|
|
552
|
+
# Check Pattern 1: MODE TYPE
|
|
553
|
+
# Try parsing next token as a built-in type (not UDT)
|
|
554
|
+
# If successful, the keyword is an identifier, not a mode
|
|
555
|
+
is_followed_by_builtin_type = self._try_parse(
|
|
556
|
+
lambda: self._advance() # type: ignore
|
|
557
|
+
or self._parse_types(check_func=False, allow_identifiers=False),
|
|
558
|
+
retreat=True,
|
|
559
|
+
)
|
|
560
|
+
if is_followed_by_builtin_type:
|
|
561
|
+
return None # Pattern: "out INT" → out is parameter name
|
|
562
|
+
|
|
563
|
+
# Check Pattern 2: MODE NAME TYPE
|
|
564
|
+
# If next token is an identifier, check if there's a type after it
|
|
565
|
+
# The type can be built-in or user-defined (allow_identifiers=True)
|
|
566
|
+
if self._next.token_type not in self.ID_VAR_TOKENS:
|
|
567
|
+
return None
|
|
568
|
+
|
|
569
|
+
is_followed_by_any_type = self._try_parse(
|
|
570
|
+
lambda: self._advance(2) # type: ignore
|
|
571
|
+
or self._parse_types(check_func=False, allow_identifiers=True),
|
|
572
|
+
retreat=True,
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
if is_followed_by_any_type:
|
|
576
|
+
return mode_token.token_type # Pattern: "OUT x INT" → OUT is mode
|
|
577
|
+
|
|
578
|
+
return None
|
|
579
|
+
|
|
580
|
+
def _create_mode_constraint(self, param_mode: TokenType) -> exp.InOutColumnConstraint:
|
|
581
|
+
"""
|
|
582
|
+
Create parameter mode constraint for function parameters.
|
|
583
|
+
|
|
584
|
+
Args:
|
|
585
|
+
param_mode: The parameter mode token (IN, OUT, INOUT, or VARIADIC).
|
|
586
|
+
|
|
587
|
+
Returns:
|
|
588
|
+
InOutColumnConstraint expression representing the parameter mode.
|
|
589
|
+
"""
|
|
590
|
+
return self.expression(
|
|
591
|
+
exp.InOutColumnConstraint,
|
|
592
|
+
input_=(param_mode in {TokenType.IN, TokenType.INOUT}),
|
|
593
|
+
output=(param_mode in {TokenType.OUT, TokenType.INOUT}),
|
|
594
|
+
variadic=(param_mode == TokenType.VARIADIC),
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
def _parse_function_parameter(self) -> t.Optional[exp.Expression]:
|
|
598
|
+
param_mode = self._parse_parameter_mode()
|
|
599
|
+
|
|
600
|
+
if param_mode:
|
|
601
|
+
self._advance()
|
|
602
|
+
|
|
603
|
+
# Parse parameter name and type
|
|
604
|
+
param_name = self._parse_id_var()
|
|
605
|
+
column_def = self._parse_column_def(this=param_name, computed_column=False)
|
|
606
|
+
|
|
607
|
+
# Attach mode as constraint
|
|
608
|
+
if param_mode and column_def:
|
|
609
|
+
constraint = self._create_mode_constraint(param_mode)
|
|
610
|
+
if not column_def.args.get("constraints"):
|
|
611
|
+
column_def.set("constraints", [])
|
|
612
|
+
column_def.args["constraints"].insert(0, constraint)
|
|
613
|
+
|
|
614
|
+
return column_def
|
|
615
|
+
|
|
511
616
|
def _parse_query_parameter(self) -> t.Optional[exp.Expression]:
|
|
512
617
|
this = (
|
|
513
618
|
self._parse_wrapped(self._parse_id_var)
|
|
@@ -517,29 +622,6 @@ class Postgres(Dialect):
|
|
|
517
622
|
self._match_text_seq("S")
|
|
518
623
|
return self.expression(exp.Placeholder, this=this)
|
|
519
624
|
|
|
520
|
-
def _parse_operator(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
|
|
521
|
-
while True:
|
|
522
|
-
if not self._match(TokenType.L_PAREN):
|
|
523
|
-
break
|
|
524
|
-
|
|
525
|
-
op = ""
|
|
526
|
-
while self._curr and not self._match(TokenType.R_PAREN):
|
|
527
|
-
op += self._curr.text
|
|
528
|
-
self._advance()
|
|
529
|
-
|
|
530
|
-
this = self.expression(
|
|
531
|
-
exp.Operator,
|
|
532
|
-
comments=self._prev_comments,
|
|
533
|
-
this=this,
|
|
534
|
-
operator=op,
|
|
535
|
-
expression=self._parse_bitwise(),
|
|
536
|
-
)
|
|
537
|
-
|
|
538
|
-
if not self._match(TokenType.OPERATOR):
|
|
539
|
-
break
|
|
540
|
-
|
|
541
|
-
return this
|
|
542
|
-
|
|
543
625
|
def _parse_date_part(self) -> exp.Expression:
|
|
544
626
|
part = self._parse_type()
|
|
545
627
|
self._match(TokenType.COMMA)
|
|
@@ -611,6 +693,7 @@ class Postgres(Dialect):
|
|
|
611
693
|
SUPPORTS_MEDIAN = False
|
|
612
694
|
ARRAY_SIZE_DIM_REQUIRED = True
|
|
613
695
|
SUPPORTS_BETWEEN_FLAGS = True
|
|
696
|
+
INOUT_SEPARATOR = "" # PostgreSQL uses "INOUT" (no space)
|
|
614
697
|
|
|
615
698
|
SUPPORTED_JSON_PATH_PARTS = {
|
|
616
699
|
exp.JSONPathKey,
|
|
@@ -618,6 +701,14 @@ class Postgres(Dialect):
|
|
|
618
701
|
exp.JSONPathSubscript,
|
|
619
702
|
}
|
|
620
703
|
|
|
704
|
+
def lateral_sql(self, expression: exp.Lateral) -> str:
|
|
705
|
+
sql = super().lateral_sql(expression)
|
|
706
|
+
|
|
707
|
+
if expression.args.get("cross_apply") is not None:
|
|
708
|
+
sql = f"{sql} ON TRUE"
|
|
709
|
+
|
|
710
|
+
return sql
|
|
711
|
+
|
|
621
712
|
TYPE_MAPPING = {
|
|
622
713
|
**generator.Generator.TYPE_MAPPING,
|
|
623
714
|
exp.DataType.Type.TINYINT: "SMALLINT",
|
|
@@ -634,8 +725,10 @@ class Postgres(Dialect):
|
|
|
634
725
|
TRANSFORMS = {
|
|
635
726
|
**generator.Generator.TRANSFORMS,
|
|
636
727
|
exp.AnyValue: _versioned_anyvalue_sql,
|
|
637
|
-
exp.ArrayConcat:
|
|
728
|
+
exp.ArrayConcat: array_concat_sql("ARRAY_CAT"),
|
|
638
729
|
exp.ArrayFilter: filter_array_using_unnest,
|
|
730
|
+
exp.ArrayAppend: array_append_sql("ARRAY_APPEND"),
|
|
731
|
+
exp.ArrayPrepend: array_append_sql("ARRAY_PREPEND", swap_params=True),
|
|
639
732
|
exp.BitwiseAndAgg: rename_func("BIT_AND"),
|
|
640
733
|
exp.BitwiseOrAgg: rename_func("BIT_OR"),
|
|
641
734
|
exp.BitwiseXor: lambda self, e: self.binary(e, "#"),
|
|
@@ -650,6 +743,7 @@ class Postgres(Dialect):
|
|
|
650
743
|
exp.DateSub: _date_add_sql("-"),
|
|
651
744
|
exp.Explode: rename_func("UNNEST"),
|
|
652
745
|
exp.ExplodingGenerateSeries: rename_func("GENERATE_SERIES"),
|
|
746
|
+
exp.Getbit: getbit_sql,
|
|
653
747
|
exp.GroupConcat: lambda self, e: groupconcat_sql(
|
|
654
748
|
self, e, func_name="STRING_AGG", within_group=False
|
|
655
749
|
),
|
|
@@ -714,7 +808,9 @@ class Postgres(Dialect):
|
|
|
714
808
|
exp.TimestampTrunc: timestamptrunc_sql(zone=True),
|
|
715
809
|
exp.TimeStrToTime: timestrtotime_sql,
|
|
716
810
|
exp.TimeToStr: lambda self, e: self.func("TO_CHAR", e.this, self.format_time(e)),
|
|
717
|
-
exp.ToChar: lambda self, e: self.function_fallback_sql(e)
|
|
811
|
+
exp.ToChar: lambda self, e: self.function_fallback_sql(e)
|
|
812
|
+
if e.args.get("format")
|
|
813
|
+
else self.tochar_sql(e),
|
|
718
814
|
exp.Trim: trim_sql,
|
|
719
815
|
exp.TryCast: no_trycast_sql,
|
|
720
816
|
exp.TsOrDsAdd: _date_add_sql("+"),
|
|
@@ -752,6 +848,18 @@ class Postgres(Dialect):
|
|
|
752
848
|
self.unsupported("Column comments are not supported in the CREATE statement")
|
|
753
849
|
return ""
|
|
754
850
|
|
|
851
|
+
def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
|
|
852
|
+
# PostgreSQL places parameter modes BEFORE parameter name
|
|
853
|
+
param_constraint = expression.find(exp.InOutColumnConstraint)
|
|
854
|
+
|
|
855
|
+
if param_constraint:
|
|
856
|
+
mode_sql = self.sql(param_constraint)
|
|
857
|
+
param_constraint.pop() # Remove to prevent double-rendering
|
|
858
|
+
base_sql = super().columndef_sql(expression, sep)
|
|
859
|
+
return f"{mode_sql} {base_sql}"
|
|
860
|
+
|
|
861
|
+
return super().columndef_sql(expression, sep)
|
|
862
|
+
|
|
755
863
|
def unnest_sql(self, expression: exp.Unnest) -> str:
|
|
756
864
|
if len(expression.expressions) == 1:
|
|
757
865
|
arg = expression.expressions[0]
|
|
@@ -865,8 +973,9 @@ class Postgres(Dialect):
|
|
|
865
973
|
def interval_sql(self, expression: exp.Interval) -> str:
|
|
866
974
|
unit = expression.text("unit").lower()
|
|
867
975
|
|
|
868
|
-
|
|
869
|
-
|
|
976
|
+
this = expression.this
|
|
977
|
+
if unit.startswith("quarter") and isinstance(this, exp.Literal):
|
|
978
|
+
this.replace(exp.Literal.string(int(this.to_py()) * 3))
|
|
870
979
|
expression.args["unit"].replace(exp.var("MONTH"))
|
|
871
980
|
|
|
872
981
|
return super().interval_sql(expression)
|
sqlglot/dialects/presto.py
CHANGED
|
@@ -267,6 +267,7 @@ class Presto(Dialect):
|
|
|
267
267
|
TABLESAMPLE_SIZE_IS_PERCENT = True
|
|
268
268
|
LOG_BASE_FIRST: t.Optional[bool] = None
|
|
269
269
|
SUPPORTS_VALUES_DEFAULT = False
|
|
270
|
+
LEAST_GREATEST_IGNORES_NULLS = False
|
|
270
271
|
|
|
271
272
|
TIME_MAPPING = MySQL.TIME_MAPPING
|
|
272
273
|
|
|
@@ -373,6 +374,7 @@ class Presto(Dialect):
|
|
|
373
374
|
"MD5": exp.MD5Digest.from_arg_list,
|
|
374
375
|
"SHA256": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(256)),
|
|
375
376
|
"SHA512": lambda args: exp.SHA2(this=seq_get(args, 0), length=exp.Literal.number(512)),
|
|
377
|
+
"WEEK": exp.WeekOfYear.from_arg_list,
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
FUNCTION_PARSERS = parser.Parser.FUNCTION_PARSERS.copy()
|
|
@@ -707,9 +709,11 @@ class Presto(Dialect):
|
|
|
707
709
|
return super().bracket_sql(expression)
|
|
708
710
|
|
|
709
711
|
def struct_sql(self, expression: exp.Struct) -> str:
|
|
710
|
-
|
|
712
|
+
if not expression.type:
|
|
713
|
+
from sqlglot.optimizer.annotate_types import annotate_types
|
|
714
|
+
|
|
715
|
+
annotate_types(expression, dialect=self.dialect)
|
|
711
716
|
|
|
712
|
-
expression = annotate_types(expression, dialect=self.dialect)
|
|
713
717
|
values: t.List[str] = []
|
|
714
718
|
schema: t.List[str] = []
|
|
715
719
|
unknown_type = False
|
sqlglot/dialects/redshift.py
CHANGED
|
@@ -5,6 +5,7 @@ import typing as t
|
|
|
5
5
|
from sqlglot import exp, transforms
|
|
6
6
|
from sqlglot.dialects.dialect import (
|
|
7
7
|
NormalizationStrategy,
|
|
8
|
+
array_concat_sql,
|
|
8
9
|
concat_to_dpipe_sql,
|
|
9
10
|
concat_ws_to_dpipe_sql,
|
|
10
11
|
date_delta_sql,
|
|
@@ -49,6 +50,7 @@ class Redshift(Postgres):
|
|
|
49
50
|
HAS_DISTINCT_ARRAY_CONSTRUCTORS = True
|
|
50
51
|
COALESCE_COMPARISON_NON_STANDARD = True
|
|
51
52
|
REGEXP_EXTRACT_POSITION_OVERFLOW_RETURNS_NULL = False
|
|
53
|
+
ARRAY_FUNCS_PROPAGATES_NULLS = True
|
|
52
54
|
|
|
53
55
|
# ref: https://docs.aws.amazon.com/redshift/latest/dg/r_FORMAT_strings.html
|
|
54
56
|
TIME_FORMAT = "'YYYY-MM-DD HH24:MI:SS'"
|
|
@@ -82,6 +84,7 @@ class Redshift(Postgres):
|
|
|
82
84
|
),
|
|
83
85
|
"STRTOL": exp.FromBase.from_arg_list,
|
|
84
86
|
}
|
|
87
|
+
FUNCTIONS.pop("GET_BIT")
|
|
85
88
|
|
|
86
89
|
NO_PAREN_FUNCTION_PARSERS = {
|
|
87
90
|
**Postgres.Parser.NO_PAREN_FUNCTION_PARSERS,
|
|
@@ -189,7 +192,7 @@ class Redshift(Postgres):
|
|
|
189
192
|
|
|
190
193
|
TRANSFORMS = {
|
|
191
194
|
**Postgres.Generator.TRANSFORMS,
|
|
192
|
-
exp.ArrayConcat:
|
|
195
|
+
exp.ArrayConcat: array_concat_sql("ARRAY_CONCAT"),
|
|
193
196
|
exp.Concat: concat_to_dpipe_sql,
|
|
194
197
|
exp.ConcatWs: concat_ws_to_dpipe_sql,
|
|
195
198
|
exp.ApproxDistinct: lambda self,
|
|
@@ -244,6 +247,9 @@ class Redshift(Postgres):
|
|
|
244
247
|
TRANSFORMS.pop(exp.LastDay)
|
|
245
248
|
TRANSFORMS.pop(exp.SHA2)
|
|
246
249
|
|
|
250
|
+
# Postgres and Redshift have different semantics for Getbit
|
|
251
|
+
TRANSFORMS.pop(exp.Getbit)
|
|
252
|
+
|
|
247
253
|
# Postgres does not permit a double precision argument in ROUND; Redshift does
|
|
248
254
|
TRANSFORMS.pop(exp.Round)
|
|
249
255
|
|
sqlglot/dialects/singlestore.py
CHANGED
|
@@ -82,6 +82,7 @@ class SingleStore(MySQL):
|
|
|
82
82
|
"::$": TokenType.DCOLONDOLLAR,
|
|
83
83
|
"::%": TokenType.DCOLONPERCENT,
|
|
84
84
|
"::?": TokenType.DCOLONQMARK,
|
|
85
|
+
"RECORD": TokenType.STRUCT,
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
class Parser(MySQL.Parser):
|
|
@@ -176,6 +177,10 @@ class SingleStore(MySQL):
|
|
|
176
177
|
expression=seq_get(args, 0),
|
|
177
178
|
json_type="JSON",
|
|
178
179
|
),
|
|
180
|
+
"JSON_KEYS": lambda args: exp.JSONKeys(
|
|
181
|
+
this=seq_get(args, 0),
|
|
182
|
+
expressions=args[1:],
|
|
183
|
+
),
|
|
179
184
|
"JSON_PRETTY": exp.JSONFormat.from_arg_list,
|
|
180
185
|
"JSON_BUILD_ARRAY": lambda args: exp.JSONArray(expressions=args),
|
|
181
186
|
"JSON_BUILD_OBJECT": lambda args: exp.JSONObject(expressions=args),
|
|
@@ -328,6 +333,7 @@ class SingleStore(MySQL):
|
|
|
328
333
|
SUPPORTS_UESCAPE = False
|
|
329
334
|
NULL_ORDERING_SUPPORTED = True
|
|
330
335
|
MATCH_AGAINST_TABLE_PREFIX = "TABLE "
|
|
336
|
+
STRUCT_DELIMITER = ("(", ")")
|
|
331
337
|
|
|
332
338
|
@staticmethod
|
|
333
339
|
def _unicode_substitute(m: re.Match[str]) -> str:
|
|
@@ -497,7 +503,6 @@ class SingleStore(MySQL):
|
|
|
497
503
|
),
|
|
498
504
|
exp.IsAscii: lambda self, e: f"({self.sql(e, 'this')} RLIKE '^[\x00-\x7f]*$')",
|
|
499
505
|
exp.MD5Digest: lambda self, e: self.func("UNHEX", self.func("MD5", e.this)),
|
|
500
|
-
exp.Chr: rename_func("CHAR"),
|
|
501
506
|
exp.Contains: rename_func("INSTR"),
|
|
502
507
|
exp.RegexpExtractAll: unsupported_args("position", "occurrence", "group")(
|
|
503
508
|
lambda self, e: self.func(
|
|
@@ -613,7 +618,6 @@ class SingleStore(MySQL):
|
|
|
613
618
|
exp.DataType.Type.SERIAL,
|
|
614
619
|
exp.DataType.Type.SMALLSERIAL,
|
|
615
620
|
exp.DataType.Type.SMALLMONEY,
|
|
616
|
-
exp.DataType.Type.STRUCT,
|
|
617
621
|
exp.DataType.Type.SUPER,
|
|
618
622
|
exp.DataType.Type.TIMETZ,
|
|
619
623
|
exp.DataType.Type.TIMESTAMPNTZ,
|
|
@@ -654,6 +658,7 @@ class SingleStore(MySQL):
|
|
|
654
658
|
exp.DataType.Type.LINESTRING: "GEOGRAPHY",
|
|
655
659
|
exp.DataType.Type.POLYGON: "GEOGRAPHY",
|
|
656
660
|
exp.DataType.Type.MULTIPOLYGON: "GEOGRAPHY",
|
|
661
|
+
exp.DataType.Type.STRUCT: "RECORD",
|
|
657
662
|
exp.DataType.Type.JSONB: "BSON",
|
|
658
663
|
exp.DataType.Type.TIMESTAMP: "TIMESTAMP",
|
|
659
664
|
exp.DataType.Type.TIMESTAMP_S: "TIMESTAMP",
|
|
@@ -1760,8 +1765,13 @@ class SingleStore(MySQL):
|
|
|
1760
1765
|
self.func("TO_JSON", expression.this),
|
|
1761
1766
|
)
|
|
1762
1767
|
|
|
1763
|
-
@unsupported_args("kind", "
|
|
1768
|
+
@unsupported_args("kind", "values")
|
|
1764
1769
|
def datatype_sql(self, expression: exp.DataType) -> str:
|
|
1770
|
+
if expression.args.get("nested") and not expression.is_type(exp.DataType.Type.STRUCT):
|
|
1771
|
+
self.unsupported(
|
|
1772
|
+
f"Argument 'nested' is not supported for representation of '{expression.this.value}' in SingleStore"
|
|
1773
|
+
)
|
|
1774
|
+
|
|
1765
1775
|
if expression.is_type(exp.DataType.Type.VARBINARY) and not expression.expressions:
|
|
1766
1776
|
# `VARBINARY` must always have a size - if it doesn't, we always generate `BLOB`
|
|
1767
1777
|
return "BLOB"
|