From c51a9844b869fd7cd69e5cc7658d34f61a865185 Mon Sep 17 00:00:00 2001
From: Daniel Baumann
Date: Wed, 1 Nov 2023 06:12:42 +0100
Subject: Merging upstream version 19.0.1.
Signed-off-by: Daniel Baumann
---
docs/sqlglot/dialects/dialect.html | 1385 ++++++++++++++++++------------------
1 file changed, 693 insertions(+), 692 deletions(-)
(limited to 'docs/sqlglot/dialects/dialect.html')
diff --git a/docs/sqlglot/dialects/dialect.html b/docs/sqlglot/dialects/dialect.html
index 2728c12..f34f887 100644
--- a/docs/sqlglot/dialects/dialect.html
+++ b/docs/sqlglot/dialects/dialect.html
@@ -424,10 +424,10 @@
isnull_to_is_null
- move_insert_cte_sql
+ generatedasidentitycolumnconstraint_sql
- generatedasidentitycolumnconstraint_sql
+ arg_max_or_min_no_count
@@ -769,210 +769,210 @@
315 ) -> t.List[t.Optional[exp.Expression]]:
316 return self.parser(**opts).parse_into(expression_type, self.tokenize(sql), sql)
317
-318 def generate(self, expression: t.Optional[exp.Expression], **opts) -> str:
-319 return self.generator(**opts).generate(expression)
+318 def generate(self, expression: exp.Expression, copy: bool = True, **opts) -> str:
+319 return self.generator(**opts).generate(expression, copy=copy)
320
321 def transpile(self, sql: str, **opts) -> t.List[str]:
-322 return [self.generate(expression, **opts) for expression in self.parse(sql)]
-323
-324 def tokenize(self, sql: str) -> t.List[Token]:
-325 return self.tokenizer.tokenize(sql)
+322 return [
+323 self.generate(expression, copy=False, **opts) if expression else ""
+324 for expression in self.parse(sql)
+325 ]
326
-327 @property
-328 def tokenizer(self) -> Tokenizer:
-329 if not hasattr(self, "_tokenizer"):
-330 self._tokenizer = self.tokenizer_class()
-331 return self._tokenizer
-332
-333 def parser(self, **opts) -> Parser:
-334 return self.parser_class(**opts)
+327 def tokenize(self, sql: str) -> t.List[Token]:
+328 return self.tokenizer.tokenize(sql)
+329
+330 @property
+331 def tokenizer(self) -> Tokenizer:
+332 if not hasattr(self, "_tokenizer"):
+333 self._tokenizer = self.tokenizer_class()
+334 return self._tokenizer
335
-336 def generator(self, **opts) -> Generator:
-337 return self.generator_class(**opts)
+336 def parser(self, **opts) -> Parser:
+337 return self.parser_class(**opts)
338
-339
-340DialectType = t.Union[str, Dialect, t.Type[Dialect], None]
+339 def generator(self, **opts) -> Generator:
+340 return self.generator_class(**opts)
341
342
-343def rename_func(name: str) -> t.Callable[[Generator, exp.Expression], str]:
-344 return lambda self, expression: self.func(name, *flatten(expression.args.values()))
+343DialectType = t.Union[str, Dialect, t.Type[Dialect], None]
+344
345
-346
-347def approx_count_distinct_sql(self: Generator, expression: exp.ApproxDistinct) -> str:
-348 if expression.args.get("accuracy"):
-349 self.unsupported("APPROX_COUNT_DISTINCT does not support accuracy")
-350 return self.func("APPROX_COUNT_DISTINCT", expression.this)
-351
-352
-353def if_sql(
-354 name: str = "IF", false_value: t.Optional[exp.Expression | str] = None
-355) -> t.Callable[[Generator, exp.If], str]:
-356 def _if_sql(self: Generator, expression: exp.If) -> str:
-357 return self.func(
-358 name,
-359 expression.this,
-360 expression.args.get("true"),
-361 expression.args.get("false") or false_value,
-362 )
-363
-364 return _if_sql
-365
+346def rename_func(name: str) -> t.Callable[[Generator, exp.Expression], str]:
+347 return lambda self, expression: self.func(name, *flatten(expression.args.values()))
+348
+349
+350def approx_count_distinct_sql(self: Generator, expression: exp.ApproxDistinct) -> str:
+351 if expression.args.get("accuracy"):
+352 self.unsupported("APPROX_COUNT_DISTINCT does not support accuracy")
+353 return self.func("APPROX_COUNT_DISTINCT", expression.this)
+354
+355
+356def if_sql(
+357 name: str = "IF", false_value: t.Optional[exp.Expression | str] = None
+358) -> t.Callable[[Generator, exp.If], str]:
+359 def _if_sql(self: Generator, expression: exp.If) -> str:
+360 return self.func(
+361 name,
+362 expression.this,
+363 expression.args.get("true"),
+364 expression.args.get("false") or false_value,
+365 )
366
-367def arrow_json_extract_sql(self: Generator, expression: exp.JSONExtract | exp.JSONBExtract) -> str:
-368 return self.binary(expression, "->")
+367 return _if_sql
+368
369
-370
-371def arrow_json_extract_scalar_sql(
-372 self: Generator, expression: exp.JSONExtractScalar | exp.JSONBExtractScalar
-373) -> str:
-374 return self.binary(expression, "->>")
-375
-376
-377def inline_array_sql(self: Generator, expression: exp.Array) -> str:
-378 return f"[{self.expressions(expression, flat=True)}]"
+370def arrow_json_extract_sql(self: Generator, expression: exp.JSONExtract | exp.JSONBExtract) -> str:
+371 return self.binary(expression, "->")
+372
+373
+374def arrow_json_extract_scalar_sql(
+375 self: Generator, expression: exp.JSONExtractScalar | exp.JSONBExtractScalar
+376) -> str:
+377 return self.binary(expression, "->>")
+378
379
-380
-381def no_ilike_sql(self: Generator, expression: exp.ILike) -> str:
-382 return self.like_sql(
-383 exp.Like(
-384 this=exp.Lower(this=expression.this.copy()), expression=expression.expression.copy()
-385 )
-386 )
-387
+380def inline_array_sql(self: Generator, expression: exp.Array) -> str:
+381 return f"[{self.expressions(expression, flat=True)}]"
+382
+383
+384def no_ilike_sql(self: Generator, expression: exp.ILike) -> str:
+385 return self.like_sql(
+386 exp.Like(this=exp.Lower(this=expression.this), expression=expression.expression)
+387 )
388
-389def no_paren_current_date_sql(self: Generator, expression: exp.CurrentDate) -> str:
-390 zone = self.sql(expression, "this")
-391 return f"CURRENT_DATE AT TIME ZONE {zone}" if zone else "CURRENT_DATE"
-392
+389
+390def no_paren_current_date_sql(self: Generator, expression: exp.CurrentDate) -> str:
+391 zone = self.sql(expression, "this")
+392 return f"CURRENT_DATE AT TIME ZONE {zone}" if zone else "CURRENT_DATE"
393
-394def no_recursive_cte_sql(self: Generator, expression: exp.With) -> str:
-395 if expression.args.get("recursive"):
-396 self.unsupported("Recursive CTEs are unsupported")
-397 expression.args["recursive"] = False
-398 return self.with_sql(expression)
-399
+394
+395def no_recursive_cte_sql(self: Generator, expression: exp.With) -> str:
+396 if expression.args.get("recursive"):
+397 self.unsupported("Recursive CTEs are unsupported")
+398 expression.args["recursive"] = False
+399 return self.with_sql(expression)
400
-401def no_safe_divide_sql(self: Generator, expression: exp.SafeDivide) -> str:
-402 n = self.sql(expression, "this")
-403 d = self.sql(expression, "expression")
-404 return f"IF({d} <> 0, {n} / {d}, NULL)"
-405
+401
+402def no_safe_divide_sql(self: Generator, expression: exp.SafeDivide) -> str:
+403 n = self.sql(expression, "this")
+404 d = self.sql(expression, "expression")
+405 return f"IF({d} <> 0, {n} / {d}, NULL)"
406
-407def no_tablesample_sql(self: Generator, expression: exp.TableSample) -> str:
-408 self.unsupported("TABLESAMPLE unsupported")
-409 return self.sql(expression.this)
-410
+407
+408def no_tablesample_sql(self: Generator, expression: exp.TableSample) -> str:
+409 self.unsupported("TABLESAMPLE unsupported")
+410 return self.sql(expression.this)
411
-412def no_pivot_sql(self: Generator, expression: exp.Pivot) -> str:
-413 self.unsupported("PIVOT unsupported")
-414 return ""
-415
+412
+413def no_pivot_sql(self: Generator, expression: exp.Pivot) -> str:
+414 self.unsupported("PIVOT unsupported")
+415 return ""
416
-417def no_trycast_sql(self: Generator, expression: exp.TryCast) -> str:
-418 return self.cast_sql(expression)
-419
+417
+418def no_trycast_sql(self: Generator, expression: exp.TryCast) -> str:
+419 return self.cast_sql(expression)
420
-421def no_properties_sql(self: Generator, expression: exp.Properties) -> str:
-422 self.unsupported("Properties unsupported")
-423 return ""
-424
+421
+422def no_properties_sql(self: Generator, expression: exp.Properties) -> str:
+423 self.unsupported("Properties unsupported")
+424 return ""
425
-426def no_comment_column_constraint_sql(
-427 self: Generator, expression: exp.CommentColumnConstraint
-428) -> str:
-429 self.unsupported("CommentColumnConstraint unsupported")
-430 return ""
-431
+426
+427def no_comment_column_constraint_sql(
+428 self: Generator, expression: exp.CommentColumnConstraint
+429) -> str:
+430 self.unsupported("CommentColumnConstraint unsupported")
+431 return ""
432
-433def no_map_from_entries_sql(self: Generator, expression: exp.MapFromEntries) -> str:
-434 self.unsupported("MAP_FROM_ENTRIES unsupported")
-435 return ""
-436
+433
+434def no_map_from_entries_sql(self: Generator, expression: exp.MapFromEntries) -> str:
+435 self.unsupported("MAP_FROM_ENTRIES unsupported")
+436 return ""
437
-438def str_position_sql(self: Generator, expression: exp.StrPosition) -> str:
-439 this = self.sql(expression, "this")
-440 substr = self.sql(expression, "substr")
-441 position = self.sql(expression, "position")
-442 if position:
-443 return f"STRPOS(SUBSTR({this}, {position}), {substr}) + {position} - 1"
-444 return f"STRPOS({this}, {substr})"
-445
+438
+439def str_position_sql(self: Generator, expression: exp.StrPosition) -> str:
+440 this = self.sql(expression, "this")
+441 substr = self.sql(expression, "substr")
+442 position = self.sql(expression, "position")
+443 if position:
+444 return f"STRPOS(SUBSTR({this}, {position}), {substr}) + {position} - 1"
+445 return f"STRPOS({this}, {substr})"
446
-447def struct_extract_sql(self: Generator, expression: exp.StructExtract) -> str:
-448 return (
-449 f"{self.sql(expression, 'this')}.{self.sql(exp.to_identifier(expression.expression.name))}"
-450 )
-451
+447
+448def struct_extract_sql(self: Generator, expression: exp.StructExtract) -> str:
+449 return (
+450 f"{self.sql(expression, 'this')}.{self.sql(exp.to_identifier(expression.expression.name))}"
+451 )
452
-453def var_map_sql(
-454 self: Generator, expression: exp.Map | exp.VarMap, map_func_name: str = "MAP"
-455) -> str:
-456 keys = expression.args["keys"]
-457 values = expression.args["values"]
-458
-459 if not isinstance(keys, exp.Array) or not isinstance(values, exp.Array):
-460 self.unsupported("Cannot convert array columns into map.")
-461 return self.func(map_func_name, keys, values)
-462
-463 args = []
-464 for key, value in zip(keys.expressions, values.expressions):
-465 args.append(self.sql(key))
-466 args.append(self.sql(value))
-467
-468 return self.func(map_func_name, *args)
-469
+453
+454def var_map_sql(
+455 self: Generator, expression: exp.Map | exp.VarMap, map_func_name: str = "MAP"
+456) -> str:
+457 keys = expression.args["keys"]
+458 values = expression.args["values"]
+459
+460 if not isinstance(keys, exp.Array) or not isinstance(values, exp.Array):
+461 self.unsupported("Cannot convert array columns into map.")
+462 return self.func(map_func_name, keys, values)
+463
+464 args = []
+465 for key, value in zip(keys.expressions, values.expressions):
+466 args.append(self.sql(key))
+467 args.append(self.sql(value))
+468
+469 return self.func(map_func_name, *args)
470
-471def format_time_lambda(
-472 exp_class: t.Type[E], dialect: str, default: t.Optional[bool | str] = None
-473) -> t.Callable[[t.List], E]:
-474 """Helper used for time expressions.
-475
-476 Args:
-477 exp_class: the expression class to instantiate.
-478 dialect: target sql dialect.
-479 default: the default format, True being time.
-480
-481 Returns:
-482 A callable that can be used to return the appropriately formatted time expression.
-483 """
-484
-485 def _format_time(args: t.List):
-486 return exp_class(
-487 this=seq_get(args, 0),
-488 format=Dialect[dialect].format_time(
-489 seq_get(args, 1)
-490 or (Dialect[dialect].TIME_FORMAT if default is True else default or None)
-491 ),
-492 )
-493
-494 return _format_time
-495
+471
+472def format_time_lambda(
+473 exp_class: t.Type[E], dialect: str, default: t.Optional[bool | str] = None
+474) -> t.Callable[[t.List], E]:
+475 """Helper used for time expressions.
+476
+477 Args:
+478 exp_class: the expression class to instantiate.
+479 dialect: target sql dialect.
+480 default: the default format, True being time.
+481
+482 Returns:
+483 A callable that can be used to return the appropriately formatted time expression.
+484 """
+485
+486 def _format_time(args: t.List):
+487 return exp_class(
+488 this=seq_get(args, 0),
+489 format=Dialect[dialect].format_time(
+490 seq_get(args, 1)
+491 or (Dialect[dialect].TIME_FORMAT if default is True else default or None)
+492 ),
+493 )
+494
+495 return _format_time
496
-497def time_format(
-498 dialect: DialectType = None,
-499) -> t.Callable[[Generator, exp.UnixToStr | exp.StrToUnix], t.Optional[str]]:
-500 def _time_format(self: Generator, expression: exp.UnixToStr | exp.StrToUnix) -> t.Optional[str]:
-501 """
-502 Returns the time format for a given expression, unless it's equivalent
-503 to the default time format of the dialect of interest.
-504 """
-505 time_format = self.format_time(expression)
-506 return time_format if time_format != Dialect.get_or_raise(dialect).TIME_FORMAT else None
-507
-508 return _time_format
-509
+497
+498def time_format(
+499 dialect: DialectType = None,
+500) -> t.Callable[[Generator, exp.UnixToStr | exp.StrToUnix], t.Optional[str]]:
+501 def _time_format(self: Generator, expression: exp.UnixToStr | exp.StrToUnix) -> t.Optional[str]:
+502 """
+503 Returns the time format for a given expression, unless it's equivalent
+504 to the default time format of the dialect of interest.
+505 """
+506 time_format = self.format_time(expression)
+507 return time_format if time_format != Dialect.get_or_raise(dialect).TIME_FORMAT else None
+508
+509 return _time_format
510
-511def create_with_partitions_sql(self: Generator, expression: exp.Create) -> str:
-512 """
-513 In Hive and Spark, the PARTITIONED BY property acts as an extension of a table's schema. When the
-514 PARTITIONED BY value is an array of column names, they are transformed into a schema. The corresponding
-515 columns are removed from the create statement.
-516 """
-517 has_schema = isinstance(expression.this, exp.Schema)
-518 is_partitionable = expression.args.get("kind") in ("TABLE", "VIEW")
-519
-520 if has_schema and is_partitionable:
-521 expression = expression.copy()
+511
+512def create_with_partitions_sql(self: Generator, expression: exp.Create) -> str:
+513 """
+514 In Hive and Spark, the PARTITIONED BY property acts as an extension of a table's schema. When the
+515 PARTITIONED BY value is an array of column names, they are transformed into a schema. The corresponding
+516 columns are removed from the create statement.
+517 """
+518 has_schema = isinstance(expression.this, exp.Schema)
+519 is_partitionable = expression.args.get("kind") in ("TABLE", "VIEW")
+520
+521 if has_schema and is_partitionable:
522 prop = expression.find(exp.PartitionedByProperty)
523 if prop and prop.this and not isinstance(prop.this, exp.Schema):
524 schema = expression.this
@@ -1037,7 +1037,7 @@
583 this = self.sql(expression, "this")
584 unit = expression.args.get("unit")
585 unit = exp.var(unit.name.upper() if unit else "DAY")
-586 interval = exp.Interval(this=expression.expression.copy(), unit=unit)
+586 interval = exp.Interval(this=expression.expression, unit=unit)
587 return f"{data_type}_{kind}({this}, {self.sql(interval)})"
588
589 return func
@@ -1075,207 +1075,206 @@
621
622
623def left_to_substring_sql(self: Generator, expression: exp.Left) -> str:
-624 expression = expression.copy()
-625 return self.sql(
-626 exp.Substring(
-627 this=expression.this, start=exp.Literal.number(1), length=expression.expression
-628 )
-629 )
+624 return self.sql(
+625 exp.Substring(
+626 this=expression.this, start=exp.Literal.number(1), length=expression.expression
+627 )
+628 )
+629
630
-631
-632def right_to_substring_sql(self: Generator, expression: exp.Left) -> str:
-633 expression = expression.copy()
-634 return self.sql(
-635 exp.Substring(
-636 this=expression.this,
-637 start=exp.Length(this=expression.this) - exp.paren(expression.expression - 1),
-638 )
-639 )
-640
-641
-642def timestrtotime_sql(self: Generator, expression: exp.TimeStrToTime) -> str:
-643 return self.sql(exp.cast(expression.this, "timestamp"))
-644
-645
-646def datestrtodate_sql(self: Generator, expression: exp.DateStrToDate) -> str:
-647 return self.sql(exp.cast(expression.this, "date"))
-648
-649
-650# Used for Presto and Duckdb which use functions that don't support charset, and assume utf-8
-651def encode_decode_sql(
-652 self: Generator, expression: exp.Expression, name: str, replace: bool = True
-653) -> str:
-654 charset = expression.args.get("charset")
-655 if charset and charset.name.lower() != "utf-8":
-656 self.unsupported(f"Expected utf-8 character set, got {charset}.")
+631def right_to_substring_sql(self: Generator, expression: exp.Left) -> str:
+632 return self.sql(
+633 exp.Substring(
+634 this=expression.this,
+635 start=exp.Length(this=expression.this) - exp.paren(expression.expression - 1),
+636 )
+637 )
+638
+639
+640def timestrtotime_sql(self: Generator, expression: exp.TimeStrToTime) -> str:
+641 return self.sql(exp.cast(expression.this, "timestamp"))
+642
+643
+644def datestrtodate_sql(self: Generator, expression: exp.DateStrToDate) -> str:
+645 return self.sql(exp.cast(expression.this, "date"))
+646
+647
+648# Used for Presto and Duckdb which use functions that don't support charset, and assume utf-8
+649def encode_decode_sql(
+650 self: Generator, expression: exp.Expression, name: str, replace: bool = True
+651) -> str:
+652 charset = expression.args.get("charset")
+653 if charset and charset.name.lower() != "utf-8":
+654 self.unsupported(f"Expected utf-8 character set, got {charset}.")
+655
+656 return self.func(name, expression.this, expression.args.get("replace") if replace else None)
657
-658 return self.func(name, expression.this, expression.args.get("replace") if replace else None)
-659
-660
-661def min_or_least(self: Generator, expression: exp.Min) -> str:
-662 name = "LEAST" if expression.expressions else "MIN"
-663 return rename_func(name)(self, expression)
-664
-665
-666def max_or_greatest(self: Generator, expression: exp.Max) -> str:
-667 name = "GREATEST" if expression.expressions else "MAX"
-668 return rename_func(name)(self, expression)
-669
-670
-671def count_if_to_sum(self: Generator, expression: exp.CountIf) -> str:
-672 cond = expression.this
-673
-674 if isinstance(expression.this, exp.Distinct):
-675 cond = expression.this.expressions[0]
-676 self.unsupported("DISTINCT is not supported when converting COUNT_IF to SUM")
+658
+659def min_or_least(self: Generator, expression: exp.Min) -> str:
+660 name = "LEAST" if expression.expressions else "MIN"
+661 return rename_func(name)(self, expression)
+662
+663
+664def max_or_greatest(self: Generator, expression: exp.Max) -> str:
+665 name = "GREATEST" if expression.expressions else "MAX"
+666 return rename_func(name)(self, expression)
+667
+668
+669def count_if_to_sum(self: Generator, expression: exp.CountIf) -> str:
+670 cond = expression.this
+671
+672 if isinstance(expression.this, exp.Distinct):
+673 cond = expression.this.expressions[0]
+674 self.unsupported("DISTINCT is not supported when converting COUNT_IF to SUM")
+675
+676 return self.func("sum", exp.func("if", cond, 1, 0))
677
-678 return self.func("sum", exp.func("if", cond.copy(), 1, 0))
-679
-680
-681def trim_sql(self: Generator, expression: exp.Trim) -> str:
-682 target = self.sql(expression, "this")
-683 trim_type = self.sql(expression, "position")
-684 remove_chars = self.sql(expression, "expression")
-685 collation = self.sql(expression, "collation")
-686
-687 # Use TRIM/LTRIM/RTRIM syntax if the expression isn't database-specific
-688 if not remove_chars and not collation:
-689 return self.trim_sql(expression)
-690
-691 trim_type = f"{trim_type} " if trim_type else ""
-692 remove_chars = f"{remove_chars} " if remove_chars else ""
-693 from_part = "FROM " if trim_type or remove_chars else ""
-694 collation = f" COLLATE {collation}" if collation else ""
-695 return f"TRIM({trim_type}{remove_chars}{from_part}{target}{collation})"
-696
-697
-698def str_to_time_sql(self: Generator, expression: exp.Expression) -> str:
-699 return self.func("STRPTIME", expression.this, self.format_time(expression))
-700
-701
-702def ts_or_ds_to_date_sql(dialect: str) -> t.Callable:
-703 def _ts_or_ds_to_date_sql(self: Generator, expression: exp.TsOrDsToDate) -> str:
-704 _dialect = Dialect.get_or_raise(dialect)
-705 time_format = self.format_time(expression)
-706 if time_format and time_format not in (_dialect.TIME_FORMAT, _dialect.DATE_FORMAT):
-707 return self.sql(
-708 exp.cast(
-709 exp.StrToTime(this=expression.this, format=expression.args["format"]),
-710 "date",
-711 )
-712 )
-713 return self.sql(exp.cast(expression.this, "date"))
+678
+679def trim_sql(self: Generator, expression: exp.Trim) -> str:
+680 target = self.sql(expression, "this")
+681 trim_type = self.sql(expression, "position")
+682 remove_chars = self.sql(expression, "expression")
+683 collation = self.sql(expression, "collation")
+684
+685 # Use TRIM/LTRIM/RTRIM syntax if the expression isn't database-specific
+686 if not remove_chars and not collation:
+687 return self.trim_sql(expression)
+688
+689 trim_type = f"{trim_type} " if trim_type else ""
+690 remove_chars = f"{remove_chars} " if remove_chars else ""
+691 from_part = "FROM " if trim_type or remove_chars else ""
+692 collation = f" COLLATE {collation}" if collation else ""
+693 return f"TRIM({trim_type}{remove_chars}{from_part}{target}{collation})"
+694
+695
+696def str_to_time_sql(self: Generator, expression: exp.Expression) -> str:
+697 return self.func("STRPTIME", expression.this, self.format_time(expression))
+698
+699
+700def ts_or_ds_to_date_sql(dialect: str) -> t.Callable:
+701 def _ts_or_ds_to_date_sql(self: Generator, expression: exp.TsOrDsToDate) -> str:
+702 _dialect = Dialect.get_or_raise(dialect)
+703 time_format = self.format_time(expression)
+704 if time_format and time_format not in (_dialect.TIME_FORMAT, _dialect.DATE_FORMAT):
+705 return self.sql(
+706 exp.cast(
+707 exp.StrToTime(this=expression.this, format=expression.args["format"]),
+708 "date",
+709 )
+710 )
+711 return self.sql(exp.cast(expression.this, "date"))
+712
+713 return _ts_or_ds_to_date_sql
714
-715 return _ts_or_ds_to_date_sql
-716
-717
-718def concat_to_dpipe_sql(self: Generator, expression: exp.Concat | exp.SafeConcat) -> str:
-719 expression = expression.copy()
-720 return self.sql(reduce(lambda x, y: exp.DPipe(this=x, expression=y), expression.expressions))
-721
-722
-723def concat_ws_to_dpipe_sql(self: Generator, expression: exp.ConcatWs) -> str:
-724 expression = expression.copy()
-725 delim, *rest_args = expression.expressions
-726 return self.sql(
-727 reduce(
-728 lambda x, y: exp.DPipe(this=x, expression=exp.DPipe(this=delim, expression=y)),
-729 rest_args,
-730 )
-731 )
-732
-733
-734def regexp_extract_sql(self: Generator, expression: exp.RegexpExtract) -> str:
-735 bad_args = list(filter(expression.args.get, ("position", "occurrence", "parameters")))
-736 if bad_args:
-737 self.unsupported(f"REGEXP_EXTRACT does not support the following arg(s): {bad_args}")
+715
+716def concat_to_dpipe_sql(self: Generator, expression: exp.Concat | exp.SafeConcat) -> str:
+717 return self.sql(reduce(lambda x, y: exp.DPipe(this=x, expression=y), expression.expressions))
+718
+719
+720def concat_ws_to_dpipe_sql(self: Generator, expression: exp.ConcatWs) -> str:
+721 delim, *rest_args = expression.expressions
+722 return self.sql(
+723 reduce(
+724 lambda x, y: exp.DPipe(this=x, expression=exp.DPipe(this=delim, expression=y)),
+725 rest_args,
+726 )
+727 )
+728
+729
+730def regexp_extract_sql(self: Generator, expression: exp.RegexpExtract) -> str:
+731 bad_args = list(filter(expression.args.get, ("position", "occurrence", "parameters")))
+732 if bad_args:
+733 self.unsupported(f"REGEXP_EXTRACT does not support the following arg(s): {bad_args}")
+734
+735 return self.func(
+736 "REGEXP_EXTRACT", expression.this, expression.expression, expression.args.get("group")
+737 )
738
-739 return self.func(
-740 "REGEXP_EXTRACT", expression.this, expression.expression, expression.args.get("group")
-741 )
-742
-743
-744def regexp_replace_sql(self: Generator, expression: exp.RegexpReplace) -> str:
-745 bad_args = list(
-746 filter(expression.args.get, ("position", "occurrence", "parameters", "modifiers"))
-747 )
-748 if bad_args:
-749 self.unsupported(f"REGEXP_REPLACE does not support the following arg(s): {bad_args}")
+739
+740def regexp_replace_sql(self: Generator, expression: exp.RegexpReplace) -> str:
+741 bad_args = list(
+742 filter(expression.args.get, ("position", "occurrence", "parameters", "modifiers"))
+743 )
+744 if bad_args:
+745 self.unsupported(f"REGEXP_REPLACE does not support the following arg(s): {bad_args}")
+746
+747 return self.func(
+748 "REGEXP_REPLACE", expression.this, expression.expression, expression.args["replacement"]
+749 )
750
-751 return self.func(
-752 "REGEXP_REPLACE", expression.this, expression.expression, expression.args["replacement"]
-753 )
-754
-755
-756def pivot_column_names(aggregations: t.List[exp.Expression], dialect: DialectType) -> t.List[str]:
-757 names = []
-758 for agg in aggregations:
-759 if isinstance(agg, exp.Alias):
-760 names.append(agg.alias)
-761 else:
-762 """
-763 This case corresponds to aggregations without aliases being used as suffixes
-764 (e.g. col_avg(foo)). We need to unquote identifiers because they're going to
-765 be quoted in the base parser's `_parse_pivot` method, due to `to_identifier`.
-766 Otherwise, we'd end up with `col_avg(`foo`)` (notice the double quotes).
-767 """
-768 agg_all_unquoted = agg.transform(
-769 lambda node: exp.Identifier(this=node.name, quoted=False)
-770 if isinstance(node, exp.Identifier)
-771 else node
-772 )
-773 names.append(agg_all_unquoted.sql(dialect=dialect, normalize_functions="lower"))
-774
-775 return names
+751
+752def pivot_column_names(aggregations: t.List[exp.Expression], dialect: DialectType) -> t.List[str]:
+753 names = []
+754 for agg in aggregations:
+755 if isinstance(agg, exp.Alias):
+756 names.append(agg.alias)
+757 else:
+758 """
+759 This case corresponds to aggregations without aliases being used as suffixes
+760 (e.g. col_avg(foo)). We need to unquote identifiers because they're going to
+761 be quoted in the base parser's `_parse_pivot` method, due to `to_identifier`.
+762 Otherwise, we'd end up with `col_avg(`foo`)` (notice the double quotes).
+763 """
+764 agg_all_unquoted = agg.transform(
+765 lambda node: exp.Identifier(this=node.name, quoted=False)
+766 if isinstance(node, exp.Identifier)
+767 else node
+768 )
+769 names.append(agg_all_unquoted.sql(dialect=dialect, normalize_functions="lower"))
+770
+771 return names
+772
+773
+774def binary_from_function(expr_type: t.Type[B]) -> t.Callable[[t.List], B]:
+775 return lambda args: expr_type(this=seq_get(args, 0), expression=seq_get(args, 1))
776
777
-778def binary_from_function(expr_type: t.Type[B]) -> t.Callable[[t.List], B]:
-779 return lambda args: expr_type(this=seq_get(args, 0), expression=seq_get(args, 1))
-780
+778# Used to represent DATE_TRUNC in Doris, Postgres and Starrocks dialects
+779def parse_timestamp_trunc(args: t.List) -> exp.TimestampTrunc:
+780 return exp.TimestampTrunc(this=seq_get(args, 1), unit=seq_get(args, 0))
781
-782# Used to represent DATE_TRUNC in Doris, Postgres and Starrocks dialects
-783def parse_timestamp_trunc(args: t.List) -> exp.TimestampTrunc:
-784 return exp.TimestampTrunc(this=seq_get(args, 1), unit=seq_get(args, 0))
+782
+783def any_value_to_max_sql(self: Generator, expression: exp.AnyValue) -> str:
+784 return self.func("MAX", expression.this)
785
786
-787def any_value_to_max_sql(self: Generator, expression: exp.AnyValue) -> str:
-788 return self.func("MAX", expression.this)
-789
-790
-791def bool_xor_sql(self: Generator, expression: exp.Xor) -> str:
-792 a = self.sql(expression.left)
-793 b = self.sql(expression.right)
-794 return f"({a} AND (NOT {b})) OR ((NOT {a}) AND {b})"
-795
+787def bool_xor_sql(self: Generator, expression: exp.Xor) -> str:
+788 a = self.sql(expression.left)
+789 b = self.sql(expression.right)
+790 return f"({a} AND (NOT {b})) OR ((NOT {a}) AND {b})"
+791
+792
+793# Used to generate JSON_OBJECT with a comma in BigQuery and MySQL instead of colon
+794def json_keyvalue_comma_sql(self: Generator, expression: exp.JSONKeyValue) -> str:
+795 return f"{self.sql(expression, 'this')}, {self.sql(expression, 'expression')}"
796
-797# Used to generate JSON_OBJECT with a comma in BigQuery and MySQL instead of colon
-798def json_keyvalue_comma_sql(self: Generator, expression: exp.JSONKeyValue) -> str:
-799 return f"{self.sql(expression, 'this')}, {self.sql(expression, 'expression')}"
-800
-801
-802def is_parse_json(expression: exp.Expression) -> bool:
-803 return isinstance(expression, exp.ParseJSON) or (
-804 isinstance(expression, exp.Cast) and expression.is_type("json")
-805 )
+797
+798def is_parse_json(expression: exp.Expression) -> bool:
+799 return isinstance(expression, exp.ParseJSON) or (
+800 isinstance(expression, exp.Cast) and expression.is_type("json")
+801 )
+802
+803
+804def isnull_to_is_null(args: t.List) -> exp.Expression:
+805 return exp.Paren(this=exp.Is(this=seq_get(args, 0), expression=exp.null()))
806
807
-808def isnull_to_is_null(args: t.List) -> exp.Expression:
-809 return exp.Paren(this=exp.Is(this=seq_get(args, 0), expression=exp.null()))
-810
-811
-812def move_insert_cte_sql(self: Generator, expression: exp.Insert) -> str:
-813 if expression.expression.args.get("with"):
-814 expression = expression.copy()
-815 expression.set("with", expression.expression.args["with"].pop())
-816 return self.insert_sql(expression)
-817
-818
-819def generatedasidentitycolumnconstraint_sql(
-820 self: Generator, expression: exp.GeneratedAsIdentityColumnConstraint
-821) -> str:
-822 start = self.sql(expression, "start") or "1"
-823 increment = self.sql(expression, "increment") or "1"
-824 return f"IDENTITY({start}, {increment})"
+808def generatedasidentitycolumnconstraint_sql(
+809 self: Generator, expression: exp.GeneratedAsIdentityColumnConstraint
+810) -> str:
+811 start = self.sql(expression, "start") or "1"
+812 increment = self.sql(expression, "increment") or "1"
+813 return f"IDENTITY({start}, {increment})"
+814
+815
+816def arg_max_or_min_no_count(name: str) -> t.Callable[[Generator, exp.ArgMax | exp.ArgMin], str]:
+817 def _arg_max_or_min_sql(self: Generator, expression: exp.ArgMax | exp.ArgMin) -> str:
+818 if expression.args.get("count"):
+819 self.unsupported(f"Only two arguments are supported in function {name}.")
+820
+821 return self.func(name, expression.this, expression.expression)
+822
+823 return _arg_max_or_min_sql
@@ -1835,26 +1834,29 @@
316 ) -> t.List[t.Optional[exp.Expression]]:
317 return self.parser(**opts).parse_into(expression_type, self.tokenize(sql), sql)
318
-319 def generate(self, expression: t.Optional[exp.Expression], **opts) -> str:
-320 return self.generator(**opts).generate(expression)
+319 def generate(self, expression: exp.Expression, copy: bool = True, **opts) -> str:
+320 return self.generator(**opts).generate(expression, copy=copy)
321
322 def transpile(self, sql: str, **opts) -> t.List[str]:
-323 return [self.generate(expression, **opts) for expression in self.parse(sql)]
-324
-325 def tokenize(self, sql: str) -> t.List[Token]:
-326 return self.tokenizer.tokenize(sql)
+323 return [
+324 self.generate(expression, copy=False, **opts) if expression else ""
+325 for expression in self.parse(sql)
+326 ]
327
-328 @property
-329 def tokenizer(self) -> Tokenizer:
-330 if not hasattr(self, "_tokenizer"):
-331 self._tokenizer = self.tokenizer_class()
-332 return self._tokenizer
-333
-334 def parser(self, **opts) -> Parser:
-335 return self.parser_class(**opts)
+328 def tokenize(self, sql: str) -> t.List[Token]:
+329 return self.tokenizer.tokenize(sql)
+330
+331 @property
+332 def tokenizer(self) -> Tokenizer:
+333 if not hasattr(self, "_tokenizer"):
+334 self._tokenizer = self.tokenizer_class()
+335 return self._tokenizer
336
-337 def generator(self, **opts) -> Generator:
-338 return self.generator_class(**opts)
+337 def parser(self, **opts) -> Parser:
+338 return self.parser_class(**opts)
+339
+340 def generator(self, **opts) -> Generator:
+341 return self.generator_class(**opts)
@@ -2443,14 +2445,14 @@ they will be normalized to lowercase regardless of being quoted or not.
- 319 def generate(self, expression: t.Optional[exp.Expression], **opts) -> str:
-320 return self.generator(**opts).generate(expression)
+ 319 def generate(self, expression: exp.Expression, copy: bool = True, **opts) -> str:
+320 return self.generator(**opts).generate(expression, copy=copy)
@@ -2469,7 +2471,10 @@ they will be normalized to lowercase regardless of being quoted or not.
322 def transpile(self, sql: str, **opts) -> t.List[str]:
-323 return [self.generate(expression, **opts) for expression in self.parse(sql)]
+323 return [
+324 self.generate(expression, copy=False, **opts) if expression else ""
+325 for expression in self.parse(sql)
+326 ]
@@ -2487,8 +2492,8 @@ they will be normalized to lowercase regardless of being quoted or not.
- 325 def tokenize(self, sql: str) -> t.List[Token]:
-326 return self.tokenizer.tokenize(sql)
+ 328 def tokenize(self, sql: str) -> t.List[Token]:
+329 return self.tokenizer.tokenize(sql)
@@ -2517,8 +2522,8 @@ they will be normalized to lowercase regardless of being quoted or not.
- 334 def parser(self, **opts) -> Parser:
-335 return self.parser_class(**opts)
+ 337 def parser(self, **opts) -> Parser:
+338 return self.parser_class(**opts)
@@ -2536,8 +2541,8 @@ they will be normalized to lowercase regardless of being quoted or not.
- 337 def generator(self, **opts) -> Generator:
-338 return self.generator_class(**opts)
+ 340 def generator(self, **opts) -> Generator:
+341 return self.generator_class(**opts)
@@ -2689,8 +2694,8 @@ they will be normalized to lowercase regardless of being quoted or not.
- 344def rename_func(name: str) -> t.Callable[[Generator, exp.Expression], str]:
-345 return lambda self, expression: self.func(name, *flatten(expression.args.values()))
+ 347def rename_func(name: str) -> t.Callable[[Generator, exp.Expression], str]:
+348 return lambda self, expression: self.func(name, *flatten(expression.args.values()))
@@ -2708,10 +2713,10 @@ they will be normalized to lowercase regardless of being quoted or not.
- 348def approx_count_distinct_sql(self: Generator, expression: exp.ApproxDistinct) -> str:
-349 if expression.args.get("accuracy"):
-350 self.unsupported("APPROX_COUNT_DISTINCT does not support accuracy")
-351 return self.func("APPROX_COUNT_DISTINCT", expression.this)
+ 351def approx_count_distinct_sql(self: Generator, expression: exp.ApproxDistinct) -> str:
+352 if expression.args.get("accuracy"):
+353 self.unsupported("APPROX_COUNT_DISTINCT does not support accuracy")
+354 return self.func("APPROX_COUNT_DISTINCT", expression.this)
@@ -2729,18 +2734,18 @@ they will be normalized to lowercase regardless of being quoted or not.
- 354def if_sql(
-355 name: str = "IF", false_value: t.Optional[exp.Expression | str] = None
-356) -> t.Callable[[Generator, exp.If], str]:
-357 def _if_sql(self: Generator, expression: exp.If) -> str:
-358 return self.func(
-359 name,
-360 expression.this,
-361 expression.args.get("true"),
-362 expression.args.get("false") or false_value,
-363 )
-364
-365 return _if_sql
+ 357def if_sql(
+358 name: str = "IF", false_value: t.Optional[exp.Expression | str] = None
+359) -> t.Callable[[Generator, exp.If], str]:
+360 def _if_sql(self: Generator, expression: exp.If) -> str:
+361 return self.func(
+362 name,
+363 expression.this,
+364 expression.args.get("true"),
+365 expression.args.get("false") or false_value,
+366 )
+367
+368 return _if_sql
@@ -2758,8 +2763,8 @@ they will be normalized to lowercase regardless of being quoted or not.
-
-
- 378def inline_array_sql(self: Generator, expression: exp.Array) -> str:
-379 return f"[{self.expressions(expression, flat=True)}]"
+ 381def inline_array_sql(self: Generator, expression: exp.Array) -> str:
+382 return f"[{self.expressions(expression, flat=True)}]"
@@ -2817,12 +2822,10 @@ they will be normalized to lowercase regardless of being quoted or not.
- 382def no_ilike_sql(self: Generator, expression: exp.ILike) -> str:
-383 return self.like_sql(
-384 exp.Like(
-385 this=exp.Lower(this=expression.this.copy()), expression=expression.expression.copy()
-386 )
-387 )
+ 385def no_ilike_sql(self: Generator, expression: exp.ILike) -> str:
+386 return self.like_sql(
+387 exp.Like(this=exp.Lower(this=expression.this), expression=expression.expression)
+388 )
@@ -2840,9 +2843,9 @@ they will be normalized to lowercase regardless of being quoted or not.
- 390def no_paren_current_date_sql(self: Generator, expression: exp.CurrentDate) -> str:
-391 zone = self.sql(expression, "this")
-392 return f"CURRENT_DATE AT TIME ZONE {zone}" if zone else "CURRENT_DATE"
+ 391def no_paren_current_date_sql(self: Generator, expression: exp.CurrentDate) -> str:
+392 zone = self.sql(expression, "this")
+393 return f"CURRENT_DATE AT TIME ZONE {zone}" if zone else "CURRENT_DATE"
@@ -2860,11 +2863,11 @@ they will be normalized to lowercase regardless of being quoted or not.
- 395def no_recursive_cte_sql(self: Generator, expression: exp.With) -> str:
-396 if expression.args.get("recursive"):
-397 self.unsupported("Recursive CTEs are unsupported")
-398 expression.args["recursive"] = False
-399 return self.with_sql(expression)
+ 396def no_recursive_cte_sql(self: Generator, expression: exp.With) -> str:
+397 if expression.args.get("recursive"):
+398 self.unsupported("Recursive CTEs are unsupported")
+399 expression.args["recursive"] = False
+400 return self.with_sql(expression)
@@ -2882,10 +2885,10 @@ they will be normalized to lowercase regardless of being quoted or not.
- 402def no_safe_divide_sql(self: Generator, expression: exp.SafeDivide) -> str:
-403 n = self.sql(expression, "this")
-404 d = self.sql(expression, "expression")
-405 return f"IF({d} <> 0, {n} / {d}, NULL)"
+ 403def no_safe_divide_sql(self: Generator, expression: exp.SafeDivide) -> str:
+404 n = self.sql(expression, "this")
+405 d = self.sql(expression, "expression")
+406 return f"IF({d} <> 0, {n} / {d}, NULL)"
@@ -2903,9 +2906,9 @@ they will be normalized to lowercase regardless of being quoted or not.
- 408def no_tablesample_sql(self: Generator, expression: exp.TableSample) -> str:
-409 self.unsupported("TABLESAMPLE unsupported")
-410 return self.sql(expression.this)
+ 409def no_tablesample_sql(self: Generator, expression: exp.TableSample) -> str:
+410 self.unsupported("TABLESAMPLE unsupported")
+411 return self.sql(expression.this)
@@ -2923,9 +2926,9 @@ they will be normalized to lowercase regardless of being quoted or not.
- 413def no_pivot_sql(self: Generator, expression: exp.Pivot) -> str:
-414 self.unsupported("PIVOT unsupported")
-415 return ""
+ 414def no_pivot_sql(self: Generator, expression: exp.Pivot) -> str:
+415 self.unsupported("PIVOT unsupported")
+416 return ""
@@ -2943,8 +2946,8 @@ they will be normalized to lowercase regardless of being quoted or not.
- 418def no_trycast_sql(self: Generator, expression: exp.TryCast) -> str:
-419 return self.cast_sql(expression)
+ 419def no_trycast_sql(self: Generator, expression: exp.TryCast) -> str:
+420 return self.cast_sql(expression)
@@ -2962,9 +2965,9 @@ they will be normalized to lowercase regardless of being quoted or not.
- 422def no_properties_sql(self: Generator, expression: exp.Properties) -> str:
-423 self.unsupported("Properties unsupported")
-424 return ""
+ 423def no_properties_sql(self: Generator, expression: exp.Properties) -> str:
+424 self.unsupported("Properties unsupported")
+425 return ""
@@ -2982,11 +2985,11 @@ they will be normalized to lowercase regardless of being quoted or not.
- 427def no_comment_column_constraint_sql(
-428 self: Generator, expression: exp.CommentColumnConstraint
-429) -> str:
-430 self.unsupported("CommentColumnConstraint unsupported")
-431 return ""
+ 428def no_comment_column_constraint_sql(
+429 self: Generator, expression: exp.CommentColumnConstraint
+430) -> str:
+431 self.unsupported("CommentColumnConstraint unsupported")
+432 return ""
@@ -3004,9 +3007,9 @@ they will be normalized to lowercase regardless of being quoted or not.
- 434def no_map_from_entries_sql(self: Generator, expression: exp.MapFromEntries) -> str:
-435 self.unsupported("MAP_FROM_ENTRIES unsupported")
-436 return ""
+ 435def no_map_from_entries_sql(self: Generator, expression: exp.MapFromEntries) -> str:
+436 self.unsupported("MAP_FROM_ENTRIES unsupported")
+437 return ""
@@ -3024,13 +3027,13 @@ they will be normalized to lowercase regardless of being quoted or not.
- 439def str_position_sql(self: Generator, expression: exp.StrPosition) -> str:
-440 this = self.sql(expression, "this")
-441 substr = self.sql(expression, "substr")
-442 position = self.sql(expression, "position")
-443 if position:
-444 return f"STRPOS(SUBSTR({this}, {position}), {substr}) + {position} - 1"
-445 return f"STRPOS({this}, {substr})"
+ 440def str_position_sql(self: Generator, expression: exp.StrPosition) -> str:
+441 this = self.sql(expression, "this")
+442 substr = self.sql(expression, "substr")
+443 position = self.sql(expression, "position")
+444 if position:
+445 return f"STRPOS(SUBSTR({this}, {position}), {substr}) + {position} - 1"
+446 return f"STRPOS({this}, {substr})"
@@ -3048,10 +3051,10 @@ they will be normalized to lowercase regardless of being quoted or not.
-
- 454def var_map_sql(
-455 self: Generator, expression: exp.Map | exp.VarMap, map_func_name: str = "MAP"
-456) -> str:
-457 keys = expression.args["keys"]
-458 values = expression.args["values"]
-459
-460 if not isinstance(keys, exp.Array) or not isinstance(values, exp.Array):
-461 self.unsupported("Cannot convert array columns into map.")
-462 return self.func(map_func_name, keys, values)
-463
-464 args = []
-465 for key, value in zip(keys.expressions, values.expressions):
-466 args.append(self.sql(key))
-467 args.append(self.sql(value))
-468
-469 return self.func(map_func_name, *args)
+ 455def var_map_sql(
+456 self: Generator, expression: exp.Map | exp.VarMap, map_func_name: str = "MAP"
+457) -> str:
+458 keys = expression.args["keys"]
+459 values = expression.args["values"]
+460
+461 if not isinstance(keys, exp.Array) or not isinstance(values, exp.Array):
+462 self.unsupported("Cannot convert array columns into map.")
+463 return self.func(map_func_name, keys, values)
+464
+465 args = []
+466 for key, value in zip(keys.expressions, values.expressions):
+467 args.append(self.sql(key))
+468 args.append(self.sql(value))
+469
+470 return self.func(map_func_name, *args)
@@ -3102,30 +3105,30 @@ they will be normalized to lowercase regardless of being quoted or not.
- 472def format_time_lambda(
-473 exp_class: t.Type[E], dialect: str, default: t.Optional[bool | str] = None
-474) -> t.Callable[[t.List], E]:
-475 """Helper used for time expressions.
-476
-477 Args:
-478 exp_class: the expression class to instantiate.
-479 dialect: target sql dialect.
-480 default: the default format, True being time.
-481
-482 Returns:
-483 A callable that can be used to return the appropriately formatted time expression.
-484 """
-485
-486 def _format_time(args: t.List):
-487 return exp_class(
-488 this=seq_get(args, 0),
-489 format=Dialect[dialect].format_time(
-490 seq_get(args, 1)
-491 or (Dialect[dialect].TIME_FORMAT if default is True else default or None)
-492 ),
-493 )
-494
-495 return _format_time
+ 473def format_time_lambda(
+474 exp_class: t.Type[E], dialect: str, default: t.Optional[bool | str] = None
+475) -> t.Callable[[t.List], E]:
+476 """Helper used for time expressions.
+477
+478 Args:
+479 exp_class: the expression class to instantiate.
+480 dialect: target sql dialect.
+481 default: the default format, True being time.
+482
+483 Returns:
+484 A callable that can be used to return the appropriately formatted time expression.
+485 """
+486
+487 def _format_time(args: t.List):
+488 return exp_class(
+489 this=seq_get(args, 0),
+490 format=Dialect[dialect].format_time(
+491 seq_get(args, 1)
+492 or (Dialect[dialect].TIME_FORMAT if default is True else default or None)
+493 ),
+494 )
+495
+496 return _format_time
@@ -3159,18 +3162,18 @@ they will be normalized to lowercase regardless of being quoted or not.
- 498def time_format(
-499 dialect: DialectType = None,
-500) -> t.Callable[[Generator, exp.UnixToStr | exp.StrToUnix], t.Optional[str]]:
-501 def _time_format(self: Generator, expression: exp.UnixToStr | exp.StrToUnix) -> t.Optional[str]:
-502 """
-503 Returns the time format for a given expression, unless it's equivalent
-504 to the default time format of the dialect of interest.
-505 """
-506 time_format = self.format_time(expression)
-507 return time_format if time_format != Dialect.get_or_raise(dialect).TIME_FORMAT else None
-508
-509 return _time_format
+ 499def time_format(
+500 dialect: DialectType = None,
+501) -> t.Callable[[Generator, exp.UnixToStr | exp.StrToUnix], t.Optional[str]]:
+502 def _time_format(self: Generator, expression: exp.UnixToStr | exp.StrToUnix) -> t.Optional[str]:
+503 """
+504 Returns the time format for a given expression, unless it's equivalent
+505 to the default time format of the dialect of interest.
+506 """
+507 time_format = self.format_time(expression)
+508 return time_format if time_format != Dialect.get_or_raise(dialect).TIME_FORMAT else None
+509
+510 return _time_format
@@ -3188,17 +3191,16 @@ they will be normalized to lowercase regardless of being quoted or not.
- 512def create_with_partitions_sql(self: Generator, expression: exp.Create) -> str:
-513 """
-514 In Hive and Spark, the PARTITIONED BY property acts as an extension of a table's schema. When the
-515 PARTITIONED BY value is an array of column names, they are transformed into a schema. The corresponding
-516 columns are removed from the create statement.
-517 """
-518 has_schema = isinstance(expression.this, exp.Schema)
-519 is_partitionable = expression.args.get("kind") in ("TABLE", "VIEW")
-520
-521 if has_schema and is_partitionable:
-522 expression = expression.copy()
+ 513def create_with_partitions_sql(self: Generator, expression: exp.Create) -> str:
+514 """
+515 In Hive and Spark, the PARTITIONED BY property acts as an extension of a table's schema. When the
+516 PARTITIONED BY value is an array of column names, they are transformed into a schema. The corresponding
+517 columns are removed from the create statement.
+518 """
+519 has_schema = isinstance(expression.this, exp.Schema)
+520 is_partitionable = expression.args.get("kind") in ("TABLE", "VIEW")
+521
+522 if has_schema and is_partitionable:
523 prop = expression.find(exp.PartitionedByProperty)
524 if prop and prop.this and not isinstance(prop.this, exp.Schema):
525 schema = expression.this
@@ -3327,7 +3329,7 @@ columns are removed from the create statement.
584 this = self.sql(expression, "this")
585 unit = expression.args.get("unit")
586 unit = exp.var(unit.name.upper() if unit else "DAY")
-587 interval = exp.Interval(this=expression.expression.copy(), unit=unit)
+587 interval = exp.Interval(this=expression.expression, unit=unit)
588 return f"{data_type}_{kind}({this}, {self.sql(interval)})"
589
590 return func
@@ -3440,12 +3442,11 @@ columns are removed from the create statement.
624def left_to_substring_sql(self: Generator, expression: exp.Left) -> str:
-625 expression = expression.copy()
-626 return self.sql(
-627 exp.Substring(
-628 this=expression.this, start=exp.Literal.number(1), length=expression.expression
-629 )
-630 )
+625 return self.sql(
+626 exp.Substring(
+627 this=expression.this, start=exp.Literal.number(1), length=expression.expression
+628 )
+629 )
@@ -3463,14 +3464,13 @@ columns are removed from the create statement.
- 633def right_to_substring_sql(self: Generator, expression: exp.Left) -> str:
-634 expression = expression.copy()
-635 return self.sql(
-636 exp.Substring(
-637 this=expression.this,
-638 start=exp.Length(this=expression.this) - exp.paren(expression.expression - 1),
-639 )
-640 )
+ 632def right_to_substring_sql(self: Generator, expression: exp.Left) -> str:
+633 return self.sql(
+634 exp.Substring(
+635 this=expression.this,
+636 start=exp.Length(this=expression.this) - exp.paren(expression.expression - 1),
+637 )
+638 )
@@ -3488,8 +3488,8 @@ columns are removed from the create statement.
- 643def timestrtotime_sql(self: Generator, expression: exp.TimeStrToTime) -> str:
-644 return self.sql(exp.cast(expression.this, "timestamp"))
+ 641def timestrtotime_sql(self: Generator, expression: exp.TimeStrToTime) -> str:
+642 return self.sql(exp.cast(expression.this, "timestamp"))
@@ -3507,8 +3507,8 @@ columns are removed from the create statement.
- 647def datestrtodate_sql(self: Generator, expression: exp.DateStrToDate) -> str:
-648 return self.sql(exp.cast(expression.this, "date"))
+ 645def datestrtodate_sql(self: Generator, expression: exp.DateStrToDate) -> str:
+646 return self.sql(exp.cast(expression.this, "date"))
@@ -3526,14 +3526,14 @@ columns are removed from the create statement.
- 652def encode_decode_sql(
-653 self: Generator, expression: exp.Expression, name: str, replace: bool = True
-654) -> str:
-655 charset = expression.args.get("charset")
-656 if charset and charset.name.lower() != "utf-8":
-657 self.unsupported(f"Expected utf-8 character set, got {charset}.")
-658
-659 return self.func(name, expression.this, expression.args.get("replace") if replace else None)
+ 650def encode_decode_sql(
+651 self: Generator, expression: exp.Expression, name: str, replace: bool = True
+652) -> str:
+653 charset = expression.args.get("charset")
+654 if charset and charset.name.lower() != "utf-8":
+655 self.unsupported(f"Expected utf-8 character set, got {charset}.")
+656
+657 return self.func(name, expression.this, expression.args.get("replace") if replace else None)
@@ -3551,9 +3551,9 @@ columns are removed from the create statement.
- 662def min_or_least(self: Generator, expression: exp.Min) -> str:
-663 name = "LEAST" if expression.expressions else "MIN"
-664 return rename_func(name)(self, expression)
+ 660def min_or_least(self: Generator, expression: exp.Min) -> str:
+661 name = "LEAST" if expression.expressions else "MIN"
+662 return rename_func(name)(self, expression)
@@ -3571,9 +3571,9 @@ columns are removed from the create statement.
- 667def max_or_greatest(self: Generator, expression: exp.Max) -> str:
-668 name = "GREATEST" if expression.expressions else "MAX"
-669 return rename_func(name)(self, expression)
+ 665def max_or_greatest(self: Generator, expression: exp.Max) -> str:
+666 name = "GREATEST" if expression.expressions else "MAX"
+667 return rename_func(name)(self, expression)
@@ -3591,14 +3591,14 @@ columns are removed from the create statement.
- 672def count_if_to_sum(self: Generator, expression: exp.CountIf) -> str:
-673 cond = expression.this
-674
-675 if isinstance(expression.this, exp.Distinct):
-676 cond = expression.this.expressions[0]
-677 self.unsupported("DISTINCT is not supported when converting COUNT_IF to SUM")
-678
-679 return self.func("sum", exp.func("if", cond.copy(), 1, 0))
+ 670def count_if_to_sum(self: Generator, expression: exp.CountIf) -> str:
+671 cond = expression.this
+672
+673 if isinstance(expression.this, exp.Distinct):
+674 cond = expression.this.expressions[0]
+675 self.unsupported("DISTINCT is not supported when converting COUNT_IF to SUM")
+676
+677 return self.func("sum", exp.func("if", cond, 1, 0))
@@ -3616,21 +3616,21 @@ columns are removed from the create statement.
- 682def trim_sql(self: Generator, expression: exp.Trim) -> str:
-683 target = self.sql(expression, "this")
-684 trim_type = self.sql(expression, "position")
-685 remove_chars = self.sql(expression, "expression")
-686 collation = self.sql(expression, "collation")
-687
-688 # Use TRIM/LTRIM/RTRIM syntax if the expression isn't database-specific
-689 if not remove_chars and not collation:
-690 return self.trim_sql(expression)
-691
-692 trim_type = f"{trim_type} " if trim_type else ""
-693 remove_chars = f"{remove_chars} " if remove_chars else ""
-694 from_part = "FROM " if trim_type or remove_chars else ""
-695 collation = f" COLLATE {collation}" if collation else ""
-696 return f"TRIM({trim_type}{remove_chars}{from_part}{target}{collation})"
+ 680def trim_sql(self: Generator, expression: exp.Trim) -> str:
+681 target = self.sql(expression, "this")
+682 trim_type = self.sql(expression, "position")
+683 remove_chars = self.sql(expression, "expression")
+684 collation = self.sql(expression, "collation")
+685
+686 # Use TRIM/LTRIM/RTRIM syntax if the expression isn't database-specific
+687 if not remove_chars and not collation:
+688 return self.trim_sql(expression)
+689
+690 trim_type = f"{trim_type} " if trim_type else ""
+691 remove_chars = f"{remove_chars} " if remove_chars else ""
+692 from_part = "FROM " if trim_type or remove_chars else ""
+693 collation = f" COLLATE {collation}" if collation else ""
+694 return f"TRIM({trim_type}{remove_chars}{from_part}{target}{collation})"
@@ -3648,8 +3648,8 @@ columns are removed from the create statement.
- 699def str_to_time_sql(self: Generator, expression: exp.Expression) -> str:
-700 return self.func("STRPTIME", expression.this, self.format_time(expression))
+ 697def str_to_time_sql(self: Generator, expression: exp.Expression) -> str:
+698 return self.func("STRPTIME", expression.this, self.format_time(expression))
@@ -3667,20 +3667,20 @@ columns are removed from the create statement.
- 703def ts_or_ds_to_date_sql(dialect: str) -> t.Callable:
-704 def _ts_or_ds_to_date_sql(self: Generator, expression: exp.TsOrDsToDate) -> str:
-705 _dialect = Dialect.get_or_raise(dialect)
-706 time_format = self.format_time(expression)
-707 if time_format and time_format not in (_dialect.TIME_FORMAT, _dialect.DATE_FORMAT):
-708 return self.sql(
-709 exp.cast(
-710 exp.StrToTime(this=expression.this, format=expression.args["format"]),
-711 "date",
-712 )
-713 )
-714 return self.sql(exp.cast(expression.this, "date"))
-715
-716 return _ts_or_ds_to_date_sql
+ 701def ts_or_ds_to_date_sql(dialect: str) -> t.Callable:
+702 def _ts_or_ds_to_date_sql(self: Generator, expression: exp.TsOrDsToDate) -> str:
+703 _dialect = Dialect.get_or_raise(dialect)
+704 time_format = self.format_time(expression)
+705 if time_format and time_format not in (_dialect.TIME_FORMAT, _dialect.DATE_FORMAT):
+706 return self.sql(
+707 exp.cast(
+708 exp.StrToTime(this=expression.this, format=expression.args["format"]),
+709 "date",
+710 )
+711 )
+712 return self.sql(exp.cast(expression.this, "date"))
+713
+714 return _ts_or_ds_to_date_sql
@@ -3698,9 +3698,8 @@ columns are removed from the create statement.
- 719def concat_to_dpipe_sql(self: Generator, expression: exp.Concat | exp.SafeConcat) -> str:
-720 expression = expression.copy()
-721 return self.sql(reduce(lambda x, y: exp.DPipe(this=x, expression=y), expression.expressions))
+ 717def concat_to_dpipe_sql(self: Generator, expression: exp.Concat | exp.SafeConcat) -> str:
+718 return self.sql(reduce(lambda x, y: exp.DPipe(this=x, expression=y), expression.expressions))
@@ -3718,15 +3717,14 @@ columns are removed from the create statement.
- 724def concat_ws_to_dpipe_sql(self: Generator, expression: exp.ConcatWs) -> str:
-725 expression = expression.copy()
-726 delim, *rest_args = expression.expressions
-727 return self.sql(
-728 reduce(
-729 lambda x, y: exp.DPipe(this=x, expression=exp.DPipe(this=delim, expression=y)),
-730 rest_args,
-731 )
-732 )
+ 721def concat_ws_to_dpipe_sql(self: Generator, expression: exp.ConcatWs) -> str:
+722 delim, *rest_args = expression.expressions
+723 return self.sql(
+724 reduce(
+725 lambda x, y: exp.DPipe(this=x, expression=exp.DPipe(this=delim, expression=y)),
+726 rest_args,
+727 )
+728 )
@@ -3744,14 +3742,14 @@ columns are removed from the create statement.
-
- 745def regexp_replace_sql(self: Generator, expression: exp.RegexpReplace) -> str:
-746 bad_args = list(
-747 filter(expression.args.get, ("position", "occurrence", "parameters", "modifiers"))
-748 )
-749 if bad_args:
-750 self.unsupported(f"REGEXP_REPLACE does not support the following arg(s): {bad_args}")
-751
-752 return self.func(
-753 "REGEXP_REPLACE", expression.this, expression.expression, expression.args["replacement"]
-754 )
+ 741def regexp_replace_sql(self: Generator, expression: exp.RegexpReplace) -> str:
+742 bad_args = list(
+743 filter(expression.args.get, ("position", "occurrence", "parameters", "modifiers"))
+744 )
+745 if bad_args:
+746 self.unsupported(f"REGEXP_REPLACE does not support the following arg(s): {bad_args}")
+747
+748 return self.func(
+749 "REGEXP_REPLACE", expression.this, expression.expression, expression.args["replacement"]
+750 )
@@ -3796,26 +3794,26 @@ columns are removed from the create statement.
- 757def pivot_column_names(aggregations: t.List[exp.Expression], dialect: DialectType) -> t.List[str]:
-758 names = []
-759 for agg in aggregations:
-760 if isinstance(agg, exp.Alias):
-761 names.append(agg.alias)
-762 else:
-763 """
-764 This case corresponds to aggregations without aliases being used as suffixes
-765 (e.g. col_avg(foo)). We need to unquote identifiers because they're going to
-766 be quoted in the base parser's `_parse_pivot` method, due to `to_identifier`.
-767 Otherwise, we'd end up with `col_avg(`foo`)` (notice the double quotes).
-768 """
-769 agg_all_unquoted = agg.transform(
-770 lambda node: exp.Identifier(this=node.name, quoted=False)
-771 if isinstance(node, exp.Identifier)
-772 else node
-773 )
-774 names.append(agg_all_unquoted.sql(dialect=dialect, normalize_functions="lower"))
-775
-776 return names
+ 753def pivot_column_names(aggregations: t.List[exp.Expression], dialect: DialectType) -> t.List[str]:
+754 names = []
+755 for agg in aggregations:
+756 if isinstance(agg, exp.Alias):
+757 names.append(agg.alias)
+758 else:
+759 """
+760 This case corresponds to aggregations without aliases being used as suffixes
+761 (e.g. col_avg(foo)). We need to unquote identifiers because they're going to
+762 be quoted in the base parser's `_parse_pivot` method, due to `to_identifier`.
+763 Otherwise, we'd end up with `col_avg(`foo`)` (notice the double quotes).
+764 """
+765 agg_all_unquoted = agg.transform(
+766 lambda node: exp.Identifier(this=node.name, quoted=False)
+767 if isinstance(node, exp.Identifier)
+768 else node
+769 )
+770 names.append(agg_all_unquoted.sql(dialect=dialect, normalize_functions="lower"))
+771
+772 return names
@@ -3833,8 +3831,8 @@ columns are removed from the create statement.
- 779def binary_from_function(expr_type: t.Type[B]) -> t.Callable[[t.List], B]:
-780 return lambda args: expr_type(this=seq_get(args, 0), expression=seq_get(args, 1))
+ 775def binary_from_function(expr_type: t.Type[B]) -> t.Callable[[t.List], B]:
+776 return lambda args: expr_type(this=seq_get(args, 0), expression=seq_get(args, 1))
@@ -3852,8 +3850,8 @@ columns are removed from the create statement.
- 784def parse_timestamp_trunc(args: t.List) -> exp.TimestampTrunc:
-785 return exp.TimestampTrunc(this=seq_get(args, 1), unit=seq_get(args, 0))
+ 780def parse_timestamp_trunc(args: t.List) -> exp.TimestampTrunc:
+781 return exp.TimestampTrunc(this=seq_get(args, 1), unit=seq_get(args, 0))
@@ -3871,8 +3869,8 @@ columns are removed from the create statement.
- 788def any_value_to_max_sql(self: Generator, expression: exp.AnyValue) -> str:
-789 return self.func("MAX", expression.this)
+ 784def any_value_to_max_sql(self: Generator, expression: exp.AnyValue) -> str:
+785 return self.func("MAX", expression.this)
@@ -3890,10 +3888,10 @@ columns are removed from the create statement.
- 792def bool_xor_sql(self: Generator, expression: exp.Xor) -> str:
-793 a = self.sql(expression.left)
-794 b = self.sql(expression.right)
-795 return f"({a} AND (NOT {b})) OR ((NOT {a}) AND {b})"
+ 788def bool_xor_sql(self: Generator, expression: exp.Xor) -> str:
+789 a = self.sql(expression.left)
+790 b = self.sql(expression.right)
+791 return f"({a} AND (NOT {b})) OR ((NOT {a}) AND {b})"
@@ -3911,8 +3909,8 @@ columns are removed from the create statement.
- 799def json_keyvalue_comma_sql(self: Generator, expression: exp.JSONKeyValue) -> str:
-800 return f"{self.sql(expression, 'this')}, {self.sql(expression, 'expression')}"
+ 795def json_keyvalue_comma_sql(self: Generator, expression: exp.JSONKeyValue) -> str:
+796 return f"{self.sql(expression, 'this')}, {self.sql(expression, 'expression')}"
@@ -3930,10 +3928,10 @@ columns are removed from the create statement.
- 803def is_parse_json(expression: exp.Expression) -> bool:
-804 return isinstance(expression, exp.ParseJSON) or (
-805 isinstance(expression, exp.Cast) and expression.is_type("json")
-806 )
+ 799def is_parse_json(expression: exp.Expression) -> bool:
+800 return isinstance(expression, exp.ParseJSON) or (
+801 isinstance(expression, exp.Cast) and expression.is_type("json")
+802 )
@@ -3951,53 +3949,56 @@ columns are removed from the create statement.
- 809def isnull_to_is_null(args: t.List) -> exp.Expression:
-810 return exp.Paren(this=exp.Is(this=seq_get(args, 0), expression=exp.null()))
+ 805def isnull_to_is_null(args: t.List) -> exp.Expression:
+806 return exp.Paren(this=exp.Is(this=seq_get(args, 0), expression=exp.null()))
-