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.

    def - generate( self, expression: Optional[sqlglot.expressions.Expression], **opts) -> str: + generate( self, expression: sqlglot.expressions.Expression, copy: bool = True, **opts) -> str:
    -
    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.

    -
    368def arrow_json_extract_sql(self: Generator, expression: exp.JSONExtract | exp.JSONBExtract) -> str:
    -369    return self.binary(expression, "->")
    +            
    371def arrow_json_extract_sql(self: Generator, expression: exp.JSONExtract | exp.JSONBExtract) -> str:
    +372    return self.binary(expression, "->")
     
    @@ -2777,10 +2782,10 @@ they will be normalized to lowercase regardless of being quoted or not.

    -
    372def arrow_json_extract_scalar_sql(
    -373    self: Generator, expression: exp.JSONExtractScalar | exp.JSONBExtractScalar
    -374) -> str:
    -375    return self.binary(expression, "->>")
    +            
    375def arrow_json_extract_scalar_sql(
    +376    self: Generator, expression: exp.JSONExtractScalar | exp.JSONBExtractScalar
    +377) -> str:
    +378    return self.binary(expression, "->>")
     
    @@ -2798,8 +2803,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.

    -
    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    )
    +            
    449def struct_extract_sql(self: Generator, expression: exp.StructExtract) -> str:
    +450    return (
    +451        f"{self.sql(expression, 'this')}.{self.sql(exp.to_identifier(expression.expression.name))}"
    +452    )
     
    @@ -3069,22 +3072,22 @@ 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.

    -
    735def regexp_extract_sql(self: Generator, expression: exp.RegexpExtract) -> str:
    -736    bad_args = list(filter(expression.args.get, ("position", "occurrence", "parameters")))
    -737    if bad_args:
    -738        self.unsupported(f"REGEXP_EXTRACT does not support the following arg(s): {bad_args}")
    -739
    -740    return self.func(
    -741        "REGEXP_EXTRACT", expression.this, expression.expression, expression.args.get("group")
    -742    )
    +            
    731def regexp_extract_sql(self: Generator, expression: exp.RegexpExtract) -> str:
    +732    bad_args = list(filter(expression.args.get, ("position", "occurrence", "parameters")))
    +733    if bad_args:
    +734        self.unsupported(f"REGEXP_EXTRACT does not support the following arg(s): {bad_args}")
    +735
    +736    return self.func(
    +737        "REGEXP_EXTRACT", expression.this, expression.expression, expression.args.get("group")
    +738    )
     
    @@ -3769,16 +3767,16 @@ 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()))
     
    -
    - +
    +
    def - move_insert_cte_sql( self: sqlglot.generator.Generator, expression: sqlglot.expressions.Insert) -> str: + generatedasidentitycolumnconstraint_sql( self: sqlglot.generator.Generator, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str: - +
    - -
    813def move_insert_cte_sql(self: Generator, expression: exp.Insert) -> str:
    -814    if expression.expression.args.get("with"):
    -815        expression = expression.copy()
    -816        expression.set("with", expression.expression.args["with"].pop())
    -817    return self.insert_sql(expression)
    +    
    +            
    809def generatedasidentitycolumnconstraint_sql(
    +810    self: Generator, expression: exp.GeneratedAsIdentityColumnConstraint
    +811) -> str:
    +812    start = self.sql(expression, "start") or "1"
    +813    increment = self.sql(expression, "increment") or "1"
    +814    return f"IDENTITY({start}, {increment})"
     
    -
    - +
    +
    def - generatedasidentitycolumnconstraint_sql( self: sqlglot.generator.Generator, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str: + arg_max_or_min_no_count( name: str) -> Callable[[sqlglot.generator.Generator, sqlglot.expressions.ArgMax | sqlglot.expressions.ArgMin], str]: - +
    - -
    820def generatedasidentitycolumnconstraint_sql(
    -821    self: Generator, expression: exp.GeneratedAsIdentityColumnConstraint
    -822) -> str:
    -823    start = self.sql(expression, "start") or "1"
    -824    increment = self.sql(expression, "increment") or "1"
    -825    return f"IDENTITY({start}, {increment})"
    +    
    +            
    817def arg_max_or_min_no_count(name: str) -> t.Callable[[Generator, exp.ArgMax | exp.ArgMin], str]:
    +818    def _arg_max_or_min_sql(self: Generator, expression: exp.ArgMax | exp.ArgMin) -> str:
    +819        if expression.args.get("count"):
    +820            self.unsupported(f"Only two arguments are supported in function {name}.")
    +821
    +822        return self.func(name, expression.this, expression.expression)
    +823
    +824    return _arg_max_or_min_sql
     
    -- cgit v1.2.3