sqlglot.generators.bigquery
1from __future__ import annotations 2 3import logging 4import re 5import typing as t 6 7from sqlglot import exp, generator, transforms 8from sqlglot.dialects.dialect import ( 9 arg_max_or_min_no_count, 10 date_add_interval_sql, 11 datestrtodate_sql, 12 filter_array_using_unnest, 13 generate_series_sql, 14 if_sql, 15 inline_array_unless_query, 16 max_or_greatest, 17 min_or_least, 18 no_ilike_sql, 19 regexp_replace_sql, 20 rename_func, 21 sha256_sql, 22 timestrtotime_sql, 23 ts_or_ds_add_cast, 24 unit_to_var, 25 strposition_sql, 26 groupconcat_sql, 27 sha2_digest_sql, 28) 29from sqlglot.generator import unsupported_args 30from sqlglot.helper import seq_get 31 32logger = logging.getLogger("sqlglot") 33 34JSON_EXTRACT_TYPE = t.Union[exp.JSONExtract, exp.JSONExtractScalar, exp.JSONExtractArray] 35 36DQUOTES_ESCAPING_JSON_FUNCTIONS = ("JSON_QUERY", "JSON_VALUE", "JSON_QUERY_ARRAY") 37 38 39def _derived_table_values_to_unnest(self: BigQueryGenerator, expression: exp.Values) -> str: 40 if not expression.find_ancestor(exp.From, exp.Join): 41 return self.values_sql(expression) 42 43 structs = [] 44 alias = expression.args.get("alias") 45 for tup in expression.find_all(exp.Tuple): 46 field_aliases = ( 47 alias.columns 48 if alias and alias.columns 49 else (f"_c{i}" for i in range(len(tup.expressions))) 50 ) 51 expressions = [ 52 exp.PropertyEQ(this=exp.to_identifier(name), expression=fld) 53 for name, fld in zip(field_aliases, tup.expressions) 54 ] 55 structs.append(exp.Struct(expressions=expressions)) 56 57 # Due to `UNNEST_COLUMN_ONLY`, it is expected that the table alias be contained in the columns expression 58 alias_name_only = exp.TableAlias(columns=[alias.this]) if alias else None 59 return self.unnest_sql( 60 exp.Unnest(expressions=[exp.array(*structs, copy=False)], alias=alias_name_only) 61 ) 62 63 64def _returnsproperty_sql(self: BigQueryGenerator, expression: exp.ReturnsProperty) -> str: 65 this = expression.this 66 if isinstance(this, exp.Schema): 67 this = f"{self.sql(this, 'this')} <{self.expressions(this)}>" 68 else: 69 this = self.sql(this) 70 return f"RETURNS {this}" 71 72 73def _create_sql(self: BigQueryGenerator, expression: exp.Create) -> str: 74 returns = expression.find(exp.ReturnsProperty) 75 if expression.kind == "FUNCTION" and returns and returns.args.get("is_table"): 76 expression.set("kind", "TABLE FUNCTION") 77 78 if isinstance(expression.expression, (exp.Subquery, exp.Literal)): 79 expression.set("expression", expression.expression.this) 80 81 return self.create_sql(expression) 82 83 84# https://issuetracker.google.com/issues/162294746 85# workaround for bigquery bug when grouping by an expression and then ordering 86# WITH x AS (SELECT 1 y) 87# SELECT y + 1 z 88# FROM x 89# GROUP BY x + 1 90# ORDER by z 91def _alias_ordered_group(expression: exp.Expr) -> exp.Expr: 92 if isinstance(expression, exp.Select): 93 group = expression.args.get("group") 94 order = expression.args.get("order") 95 96 if group and order: 97 aliases = { 98 select.this: select.args["alias"] 99 for select in expression.selects 100 if isinstance(select, exp.Alias) 101 } 102 103 for grouped in group.expressions: 104 if grouped.is_int: 105 continue 106 alias = aliases.get(grouped) 107 if alias: 108 grouped.replace(exp.column(alias)) 109 110 return expression 111 112 113def _pushdown_cte_column_names(expression: exp.Expr) -> exp.Expr: 114 """BigQuery doesn't allow column names when defining a CTE, so we try to push them down.""" 115 if isinstance(expression, exp.CTE) and expression.alias_column_names: 116 cte_query = expression.this 117 118 if cte_query.is_star: 119 logger.warning( 120 "Can't push down CTE column names for star queries. Run the query through" 121 " the optimizer or use 'qualify' to expand the star projections first." 122 ) 123 return expression 124 125 column_names = expression.alias_column_names 126 expression.args["alias"].set("columns", None) 127 128 for name, select in zip(column_names, cte_query.selects): 129 to_replace = select 130 131 if isinstance(select, exp.Alias): 132 select = select.this 133 134 # Inner aliases are shadowed by the CTE column names 135 to_replace.replace(exp.alias_(select, name)) 136 137 return expression 138 139 140def _array_contains_sql(self: BigQueryGenerator, expression: exp.ArrayContains) -> str: 141 return self.sql( 142 exp.Exists( 143 this=exp.select("1") 144 .from_(exp.Unnest(expressions=[expression.left]).as_("_unnest", table=["_col"])) 145 .where(exp.column("_col").eq(expression.right)) 146 ) 147 ) 148 149 150def _ts_or_ds_add_sql(self: BigQueryGenerator, expression: exp.TsOrDsAdd) -> str: 151 return date_add_interval_sql("DATE", "ADD")(self, ts_or_ds_add_cast(expression)) 152 153 154def _ts_or_ds_diff_sql(self: BigQueryGenerator, expression: exp.TsOrDsDiff) -> str: 155 expression.this.replace(exp.cast(expression.this, exp.DType.TIMESTAMP)) 156 expression.expression.replace(exp.cast(expression.expression, exp.DType.TIMESTAMP)) 157 unit = unit_to_var(expression) 158 return self.func("DATE_DIFF", expression.this, expression.expression, unit) 159 160 161def _unix_to_time_sql(self: BigQueryGenerator, expression: exp.UnixToTime) -> str: 162 scale = expression.args.get("scale") 163 timestamp = expression.this 164 165 if scale in (None, exp.UnixToTime.SECONDS): 166 return self.func("TIMESTAMP_SECONDS", timestamp) 167 if scale == exp.UnixToTime.MILLIS: 168 return self.func("TIMESTAMP_MILLIS", timestamp) 169 if scale == exp.UnixToTime.MICROS: 170 return self.func("TIMESTAMP_MICROS", timestamp) 171 172 unix_seconds = exp.cast( 173 exp.Div(this=timestamp, expression=exp.func("POW", 10, scale)), exp.DType.BIGINT 174 ) 175 return self.func("TIMESTAMP_SECONDS", unix_seconds) 176 177 178def _str_to_datetime_sql(self: BigQueryGenerator, expression: exp.StrToDate | exp.StrToTime) -> str: 179 this = self.sql(expression, "this") 180 dtype = "DATE" if isinstance(expression, exp.StrToDate) else "TIMESTAMP" 181 182 if expression.args.get("safe"): 183 fmt = self.format_time( 184 expression, 185 self.dialect.INVERSE_FORMAT_MAPPING, 186 self.dialect.INVERSE_FORMAT_TRIE, 187 ) 188 return f"SAFE_CAST({this} AS {dtype} FORMAT {fmt})" 189 190 fmt = self.format_time(expression) 191 return self.func(f"PARSE_{dtype}", fmt, this, expression.args.get("zone")) 192 193 194@unsupported_args("ins_cost", "del_cost", "sub_cost") 195def _levenshtein_sql(self: BigQueryGenerator, expression: exp.Levenshtein) -> str: 196 max_dist = expression.args.get("max_dist") 197 if max_dist: 198 max_dist = exp.Kwarg(this=exp.var("max_distance"), expression=max_dist) 199 200 return self.func("EDIT_DISTANCE", expression.this, expression.expression, max_dist) 201 202 203def _json_extract_sql(self: BigQueryGenerator, expression: JSON_EXTRACT_TYPE) -> str: 204 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 205 upper = name.upper() 206 207 dquote_escaping = upper in DQUOTES_ESCAPING_JSON_FUNCTIONS 208 209 if dquote_escaping: 210 self._quote_json_path_key_using_brackets = False 211 212 sql = rename_func(upper)(self, expression) 213 214 if dquote_escaping: 215 self._quote_json_path_key_using_brackets = True 216 217 return sql 218 219 220class BigQueryGenerator(generator.Generator): 221 TRY_SUPPORTED = False 222 SUPPORTS_UESCAPE = False 223 SUPPORTS_DECODE_CASE = False 224 INTERVAL_ALLOWS_PLURAL_FORM = False 225 JOIN_HINTS = False 226 QUERY_HINTS = False 227 TABLE_HINTS = False 228 LIMIT_FETCH = "LIMIT" 229 RENAME_TABLE_WITH_DB = False 230 NVL2_SUPPORTED = False 231 UNNEST_WITH_ORDINALITY = False 232 COLLATE_IS_FUNC = True 233 LIMIT_ONLY_LITERALS = True 234 SUPPORTS_TABLE_ALIAS_COLUMNS = False 235 SUPPORTS_NAMED_CTE_COLUMNS = False 236 UNPIVOT_ALIASES_ARE_IDENTIFIERS = False 237 JSON_KEY_VALUE_PAIR_SEP = "," 238 NULL_ORDERING_SUPPORTED: bool | None = False 239 IGNORE_NULLS_IN_FUNC = True 240 JSON_PATH_SINGLE_QUOTE_ESCAPE = True 241 CAN_IMPLEMENT_ARRAY_ANY = True 242 SUPPORTS_TO_NUMBER = False 243 NAMED_PLACEHOLDER_TOKEN = "@" 244 HEX_FUNC = "TO_HEX" 245 WITH_PROPERTIES_PREFIX = "OPTIONS" 246 SUPPORTS_EXPLODING_PROJECTIONS = False 247 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 248 SUPPORTS_UNIX_SECONDS = True 249 DECLARE_DEFAULT_ASSIGNMENT = "DEFAULT" 250 251 SAFE_JSON_PATH_KEY_RE = re.compile(r"^[\-\w]*$") 252 253 WINDOW_FUNCS_WITH_NULL_ORDERING = ( 254 exp.CumeDist, 255 exp.DenseRank, 256 exp.FirstValue, 257 exp.Lag, 258 exp.LastValue, 259 exp.Lead, 260 exp.NthValue, 261 exp.Ntile, 262 exp.PercentRank, 263 exp.Rank, 264 exp.RowNumber, 265 ) 266 267 TS_OR_DS_TYPES = ( 268 exp.TsOrDsToDatetime, 269 exp.TsOrDsToTimestamp, 270 exp.TsOrDsToTime, 271 exp.TsOrDsToDate, 272 ) 273 274 TRANSFORMS = { 275 **generator.Generator.TRANSFORMS, 276 exp.AIEmbed: rename_func("EMBED"), 277 exp.AIGenerate: rename_func("GENERATE"), 278 exp.AISimilarity: rename_func("SIMILARITY"), 279 exp.ApproxTopK: rename_func("APPROX_TOP_COUNT"), 280 exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"), 281 exp.ArgMax: arg_max_or_min_no_count("MAX_BY"), 282 exp.ArgMin: arg_max_or_min_no_count("MIN_BY"), 283 exp.Array: inline_array_unless_query, 284 exp.ArrayContains: _array_contains_sql, 285 exp.ArrayFilter: filter_array_using_unnest, 286 exp.ArrayRemove: filter_array_using_unnest, 287 exp.BitwiseAndAgg: rename_func("BIT_AND"), 288 exp.BitwiseOrAgg: rename_func("BIT_OR"), 289 exp.BitwiseXorAgg: rename_func("BIT_XOR"), 290 exp.BitwiseCount: rename_func("BIT_COUNT"), 291 exp.ByteLength: rename_func("BYTE_LENGTH"), 292 exp.Cast: transforms.preprocess([transforms.remove_precision_parameterized_types]), 293 exp.CollateProperty: lambda self, e: ( 294 f"DEFAULT COLLATE {self.sql(e, 'this')}" 295 if e.args.get("default") 296 else f"COLLATE {self.sql(e, 'this')}" 297 ), 298 exp.Commit: lambda *_: "COMMIT TRANSACTION", 299 exp.CountIf: rename_func("COUNTIF"), 300 exp.Create: _create_sql, 301 exp.CTE: transforms.preprocess([_pushdown_cte_column_names]), 302 exp.DateAdd: date_add_interval_sql("DATE", "ADD"), 303 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", e.this, e.expression, unit_to_var(e)), 304 exp.DateFromParts: rename_func("DATE"), 305 exp.DateStrToDate: datestrtodate_sql, 306 exp.DateSub: date_add_interval_sql("DATE", "SUB"), 307 exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"), 308 exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"), 309 exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"), 310 exp.FromTimeZone: lambda self, e: self.func( 311 "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'" 312 ), 313 exp.GenerateSeries: generate_series_sql("GENERATE_ARRAY"), 314 exp.GroupConcat: lambda self, e: groupconcat_sql( 315 self, e, func_name="STRING_AGG", within_group=False, sep=None 316 ), 317 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 318 exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"), 319 exp.If: if_sql(false_value="NULL"), 320 exp.ILike: no_ilike_sql, 321 exp.IntDiv: rename_func("DIV"), 322 exp.Int64: rename_func("INT64"), 323 exp.JSONBool: rename_func("BOOL"), 324 exp.JSONExtract: _json_extract_sql, 325 exp.JSONExtractArray: _json_extract_sql, 326 exp.JSONExtractScalar: _json_extract_sql, 327 exp.JSONFormat: lambda self, e: self.func( 328 "TO_JSON" if e.args.get("to_json") else "TO_JSON_STRING", 329 e.this, 330 e.args.get("options"), 331 ), 332 exp.JSONKeysAtDepth: rename_func("JSON_KEYS"), 333 exp.JSONValueArray: rename_func("JSON_VALUE_ARRAY"), 334 exp.Levenshtein: _levenshtein_sql, 335 exp.Max: max_or_greatest, 336 exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)), 337 exp.MD5Digest: rename_func("MD5"), 338 exp.Min: min_or_least, 339 exp.Normalize: lambda self, e: self.func( 340 "NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE", 341 e.this, 342 e.args.get("form"), 343 ), 344 exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}", 345 exp.RegexpExtract: lambda self, e: self.func( 346 "REGEXP_EXTRACT", 347 e.this, 348 e.expression, 349 e.args.get("position"), 350 e.args.get("occurrence"), 351 ), 352 exp.RegexpExtractAll: lambda self, e: self.func("REGEXP_EXTRACT_ALL", e.this, e.expression), 353 exp.RegexpReplace: regexp_replace_sql, 354 exp.RegexpLike: rename_func("REGEXP_CONTAINS"), 355 exp.ReturnsProperty: _returnsproperty_sql, 356 exp.Rollback: lambda *_: "ROLLBACK TRANSACTION", 357 exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this), 358 exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this), 359 exp.Select: transforms.preprocess( 360 [ 361 transforms.explode_projection_to_unnest(), 362 transforms.unqualify_unnest, 363 transforms.eliminate_distinct_on, 364 _alias_ordered_group, 365 transforms.eliminate_semi_and_anti_joins, 366 ] 367 ), 368 exp.SHA: rename_func("SHA1"), 369 exp.SHA2: sha256_sql, 370 exp.SHA1Digest: rename_func("SHA1"), 371 exp.SHA2Digest: sha2_digest_sql, 372 exp.StabilityProperty: lambda self, e: ( 373 "DETERMINISTIC" if e.name == "IMMUTABLE" else "NOT DETERMINISTIC" 374 ), 375 exp.String: rename_func("STRING"), 376 exp.StrPosition: lambda self, e: strposition_sql( 377 self, e, func_name="INSTR", supports_position=True, supports_occurrence=True 378 ), 379 exp.StrToDate: _str_to_datetime_sql, 380 exp.StrToTime: _str_to_datetime_sql, 381 exp.SessionUser: lambda *_: "SESSION_USER()", 382 exp.TimeAdd: date_add_interval_sql("TIME", "ADD"), 383 exp.TimeFromParts: rename_func("TIME"), 384 exp.TimestampFromParts: rename_func("DATETIME"), 385 exp.TimeSub: date_add_interval_sql("TIME", "SUB"), 386 exp.TimestampAdd: date_add_interval_sql("TIMESTAMP", "ADD"), 387 exp.TimestampDiff: rename_func("TIMESTAMP_DIFF"), 388 exp.TimestampSub: date_add_interval_sql("TIMESTAMP", "SUB"), 389 exp.TimeStrToTime: timestrtotime_sql, 390 exp.Transaction: lambda *_: "BEGIN TRANSACTION", 391 exp.TsOrDsAdd: _ts_or_ds_add_sql, 392 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 393 exp.TsOrDsToTime: rename_func("TIME"), 394 exp.TsOrDsToDatetime: rename_func("DATETIME"), 395 exp.TsOrDsToTimestamp: rename_func("TIMESTAMP"), 396 exp.Unhex: rename_func("FROM_HEX"), 397 exp.UnixDate: rename_func("UNIX_DATE"), 398 exp.UnixToTime: _unix_to_time_sql, 399 exp.Uuid: lambda *_: "GENERATE_UUID()", 400 exp.Values: _derived_table_values_to_unnest, 401 exp.VariancePop: rename_func("VAR_POP"), 402 exp.SafeDivide: rename_func("SAFE_DIVIDE"), 403 } 404 405 SUPPORTED_JSON_PATH_PARTS = { 406 exp.JSONPathKey, 407 exp.JSONPathRoot, 408 exp.JSONPathSubscript, 409 } 410 411 TYPE_MAPPING = { 412 **generator.Generator.TYPE_MAPPING, 413 exp.DType.BIGDECIMAL: "BIGNUMERIC", 414 exp.DType.BIGINT: "INT64", 415 exp.DType.BINARY: "BYTES", 416 exp.DType.BLOB: "BYTES", 417 exp.DType.BOOLEAN: "BOOL", 418 exp.DType.CHAR: "STRING", 419 exp.DType.DECIMAL: "NUMERIC", 420 exp.DType.DOUBLE: "FLOAT64", 421 exp.DType.FLOAT: "FLOAT64", 422 exp.DType.INT: "INT64", 423 exp.DType.NCHAR: "STRING", 424 exp.DType.NVARCHAR: "STRING", 425 exp.DType.SMALLINT: "INT64", 426 exp.DType.TEXT: "STRING", 427 exp.DType.TIMESTAMP: "DATETIME", 428 exp.DType.TIMESTAMPNTZ: "DATETIME", 429 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 430 exp.DType.TIMESTAMPLTZ: "TIMESTAMP", 431 exp.DType.TINYINT: "INT64", 432 exp.DType.ROWVERSION: "BYTES", 433 exp.DType.UUID: "STRING", 434 exp.DType.VARBINARY: "BYTES", 435 exp.DType.VARCHAR: "STRING", 436 exp.DType.VARIANT: "ANY TYPE", 437 } 438 439 PROPERTIES_LOCATION = { 440 **generator.Generator.PROPERTIES_LOCATION, 441 exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 443 } 444 445 # WINDOW comes after QUALIFY 446 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#window_clause 447 # BigQuery requires QUALIFY before WINDOW 448 AFTER_HAVING_MODIFIER_TRANSFORMS = { 449 "qualify": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["qualify"], 450 "windows": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["windows"], 451 } 452 453 # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords 454 RESERVED_KEYWORDS = { 455 "all", 456 "and", 457 "any", 458 "array", 459 "as", 460 "asc", 461 "assert_rows_modified", 462 "at", 463 "between", 464 "by", 465 "case", 466 "cast", 467 "collate", 468 "contains", 469 "create", 470 "cross", 471 "cube", 472 "current", 473 "default", 474 "define", 475 "desc", 476 "distinct", 477 "else", 478 "end", 479 "enum", 480 "escape", 481 "except", 482 "exclude", 483 "exists", 484 "extract", 485 "false", 486 "fetch", 487 "following", 488 "for", 489 "from", 490 "full", 491 "group", 492 "grouping", 493 "groups", 494 "hash", 495 "having", 496 "if", 497 "ignore", 498 "in", 499 "inner", 500 "intersect", 501 "interval", 502 "into", 503 "is", 504 "join", 505 "lateral", 506 "left", 507 "like", 508 "limit", 509 "lookup", 510 "merge", 511 "natural", 512 "new", 513 "no", 514 "not", 515 "null", 516 "nulls", 517 "of", 518 "on", 519 "or", 520 "order", 521 "outer", 522 "over", 523 "partition", 524 "preceding", 525 "proto", 526 "qualify", 527 "range", 528 "recursive", 529 "respect", 530 "right", 531 "rollup", 532 "rows", 533 "select", 534 "set", 535 "some", 536 "struct", 537 "tablesample", 538 "then", 539 "to", 540 "treat", 541 "true", 542 "unbounded", 543 "union", 544 "unnest", 545 "using", 546 "when", 547 "where", 548 "window", 549 "with", 550 "within", 551 } 552 553 def datetrunc_sql(self, expression: exp.DateTrunc) -> str: 554 unit = expression.unit 555 unit_sql = unit.name if unit.is_string else self.sql(unit) 556 return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone")) 557 558 def mod_sql(self, expression: exp.Mod) -> str: 559 this = expression.this 560 expr = expression.expression 561 return self.func( 562 "MOD", 563 this.unnest() if isinstance(this, exp.Paren) else this, 564 expr.unnest() if isinstance(expr, exp.Paren) else expr, 565 ) 566 567 def column_parts(self, expression: exp.Column) -> str: 568 if expression.meta.get("quoted_column"): 569 # If a column reference is of the form `dataset.table`.name, we need 570 # to preserve the quoted table path, otherwise the reference breaks 571 table_parts = ".".join(p.name for p in expression.parts[:-1]) 572 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 573 return f"{table_path}.{self.sql(expression, 'this')}" 574 575 return super().column_parts(expression) 576 577 def table_parts(self, expression: exp.Table) -> str: 578 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 579 # we need to make sure the correct quoting is used in each case. 580 # 581 # For example, if there is a CTE x that clashes with a schema name, then the former will 582 # return the table y in that schema, whereas the latter will return the CTE's y column: 583 # 584 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 585 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 586 if expression.meta.get("quoted_table"): 587 table_parts = ".".join(p.name for p in expression.parts) 588 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 589 590 return super().table_parts(expression) 591 592 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 593 this = expression.this 594 if isinstance(this, exp.TsOrDsToDatetime): 595 func_name = "FORMAT_DATETIME" 596 elif isinstance(this, exp.TsOrDsToTimestamp): 597 func_name = "FORMAT_TIMESTAMP" 598 elif isinstance(this, exp.TsOrDsToTime): 599 func_name = "FORMAT_TIME" 600 else: 601 func_name = "FORMAT_DATE" 602 603 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 604 return self.func( 605 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 606 ) 607 608 def eq_sql(self, expression: exp.EQ) -> str: 609 # Operands of = cannot be NULL in BigQuery 610 if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null): 611 if not isinstance(expression.parent, exp.Update): 612 return "NULL" 613 614 return self.binary(expression, "=") 615 616 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 617 parent = expression.parent 618 619 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 620 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 621 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 622 return self.func( 623 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 624 ) 625 626 return super().attimezone_sql(expression) 627 628 def trycast_sql(self, expression: exp.TryCast) -> str: 629 return self.cast_sql(expression, safe_prefix="SAFE_") 630 631 def bracket_sql(self, expression: exp.Bracket) -> str: 632 this = expression.this 633 expressions = expression.expressions 634 635 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 636 arg = expressions[0] 637 if arg.type is None: 638 from sqlglot.optimizer.annotate_types import annotate_types 639 640 arg = annotate_types(arg, dialect=self.dialect) 641 642 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 643 # BQ doesn't support bracket syntax with string values for structs 644 return f"{self.sql(this)}.{arg.name}" 645 646 expressions_sql = self.expressions(expression, flat=True) 647 offset = expression.args.get("offset") 648 649 if offset == 0: 650 expressions_sql = f"OFFSET({expressions_sql})" 651 elif offset == 1: 652 expressions_sql = f"ORDINAL({expressions_sql})" 653 elif offset is not None: 654 self.unsupported(f"Unsupported array offset: {offset}") 655 656 if expression.args.get("safe"): 657 expressions_sql = f"SAFE_{expressions_sql}" 658 659 return f"{self.sql(this)}[{expressions_sql}]" 660 661 def in_unnest_op(self, expression: exp.Unnest) -> str: 662 return self.sql(expression) 663 664 def version_sql(self, expression: exp.Version) -> str: 665 if expression.name == "TIMESTAMP": 666 expression.set("this", "SYSTEM_TIME") 667 return super().version_sql(expression) 668 669 def contains_sql(self, expression: exp.Contains) -> str: 670 this = expression.this 671 expr = expression.expression 672 673 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 674 this = this.this 675 expr = expr.this 676 677 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope")) 678 679 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 680 this = expression.this 681 682 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 683 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 684 # because they aren't literals and so the above syntax is invalid BigQuery. 685 if isinstance(this, exp.Array): 686 elem = seq_get(this.expressions, 0) 687 if not (elem and elem.find(exp.Query)): 688 return f"{self.sql(expression, 'to')}{self.sql(this)}" 689 690 return super().cast_sql(expression, safe_prefix=safe_prefix)
logger =
<Logger sqlglot (WARNING)>
JSON_EXTRACT_TYPE =
typing.Union[sqlglot.expressions.json.JSONExtract, sqlglot.expressions.json.JSONExtractScalar, sqlglot.expressions.json.JSONExtractArray]
DQUOTES_ESCAPING_JSON_FUNCTIONS =
('JSON_QUERY', 'JSON_VALUE', 'JSON_QUERY_ARRAY')
221class BigQueryGenerator(generator.Generator): 222 TRY_SUPPORTED = False 223 SUPPORTS_UESCAPE = False 224 SUPPORTS_DECODE_CASE = False 225 INTERVAL_ALLOWS_PLURAL_FORM = False 226 JOIN_HINTS = False 227 QUERY_HINTS = False 228 TABLE_HINTS = False 229 LIMIT_FETCH = "LIMIT" 230 RENAME_TABLE_WITH_DB = False 231 NVL2_SUPPORTED = False 232 UNNEST_WITH_ORDINALITY = False 233 COLLATE_IS_FUNC = True 234 LIMIT_ONLY_LITERALS = True 235 SUPPORTS_TABLE_ALIAS_COLUMNS = False 236 SUPPORTS_NAMED_CTE_COLUMNS = False 237 UNPIVOT_ALIASES_ARE_IDENTIFIERS = False 238 JSON_KEY_VALUE_PAIR_SEP = "," 239 NULL_ORDERING_SUPPORTED: bool | None = False 240 IGNORE_NULLS_IN_FUNC = True 241 JSON_PATH_SINGLE_QUOTE_ESCAPE = True 242 CAN_IMPLEMENT_ARRAY_ANY = True 243 SUPPORTS_TO_NUMBER = False 244 NAMED_PLACEHOLDER_TOKEN = "@" 245 HEX_FUNC = "TO_HEX" 246 WITH_PROPERTIES_PREFIX = "OPTIONS" 247 SUPPORTS_EXPLODING_PROJECTIONS = False 248 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 249 SUPPORTS_UNIX_SECONDS = True 250 DECLARE_DEFAULT_ASSIGNMENT = "DEFAULT" 251 252 SAFE_JSON_PATH_KEY_RE = re.compile(r"^[\-\w]*$") 253 254 WINDOW_FUNCS_WITH_NULL_ORDERING = ( 255 exp.CumeDist, 256 exp.DenseRank, 257 exp.FirstValue, 258 exp.Lag, 259 exp.LastValue, 260 exp.Lead, 261 exp.NthValue, 262 exp.Ntile, 263 exp.PercentRank, 264 exp.Rank, 265 exp.RowNumber, 266 ) 267 268 TS_OR_DS_TYPES = ( 269 exp.TsOrDsToDatetime, 270 exp.TsOrDsToTimestamp, 271 exp.TsOrDsToTime, 272 exp.TsOrDsToDate, 273 ) 274 275 TRANSFORMS = { 276 **generator.Generator.TRANSFORMS, 277 exp.AIEmbed: rename_func("EMBED"), 278 exp.AIGenerate: rename_func("GENERATE"), 279 exp.AISimilarity: rename_func("SIMILARITY"), 280 exp.ApproxTopK: rename_func("APPROX_TOP_COUNT"), 281 exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"), 282 exp.ArgMax: arg_max_or_min_no_count("MAX_BY"), 283 exp.ArgMin: arg_max_or_min_no_count("MIN_BY"), 284 exp.Array: inline_array_unless_query, 285 exp.ArrayContains: _array_contains_sql, 286 exp.ArrayFilter: filter_array_using_unnest, 287 exp.ArrayRemove: filter_array_using_unnest, 288 exp.BitwiseAndAgg: rename_func("BIT_AND"), 289 exp.BitwiseOrAgg: rename_func("BIT_OR"), 290 exp.BitwiseXorAgg: rename_func("BIT_XOR"), 291 exp.BitwiseCount: rename_func("BIT_COUNT"), 292 exp.ByteLength: rename_func("BYTE_LENGTH"), 293 exp.Cast: transforms.preprocess([transforms.remove_precision_parameterized_types]), 294 exp.CollateProperty: lambda self, e: ( 295 f"DEFAULT COLLATE {self.sql(e, 'this')}" 296 if e.args.get("default") 297 else f"COLLATE {self.sql(e, 'this')}" 298 ), 299 exp.Commit: lambda *_: "COMMIT TRANSACTION", 300 exp.CountIf: rename_func("COUNTIF"), 301 exp.Create: _create_sql, 302 exp.CTE: transforms.preprocess([_pushdown_cte_column_names]), 303 exp.DateAdd: date_add_interval_sql("DATE", "ADD"), 304 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", e.this, e.expression, unit_to_var(e)), 305 exp.DateFromParts: rename_func("DATE"), 306 exp.DateStrToDate: datestrtodate_sql, 307 exp.DateSub: date_add_interval_sql("DATE", "SUB"), 308 exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"), 309 exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"), 310 exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"), 311 exp.FromTimeZone: lambda self, e: self.func( 312 "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'" 313 ), 314 exp.GenerateSeries: generate_series_sql("GENERATE_ARRAY"), 315 exp.GroupConcat: lambda self, e: groupconcat_sql( 316 self, e, func_name="STRING_AGG", within_group=False, sep=None 317 ), 318 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 319 exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"), 320 exp.If: if_sql(false_value="NULL"), 321 exp.ILike: no_ilike_sql, 322 exp.IntDiv: rename_func("DIV"), 323 exp.Int64: rename_func("INT64"), 324 exp.JSONBool: rename_func("BOOL"), 325 exp.JSONExtract: _json_extract_sql, 326 exp.JSONExtractArray: _json_extract_sql, 327 exp.JSONExtractScalar: _json_extract_sql, 328 exp.JSONFormat: lambda self, e: self.func( 329 "TO_JSON" if e.args.get("to_json") else "TO_JSON_STRING", 330 e.this, 331 e.args.get("options"), 332 ), 333 exp.JSONKeysAtDepth: rename_func("JSON_KEYS"), 334 exp.JSONValueArray: rename_func("JSON_VALUE_ARRAY"), 335 exp.Levenshtein: _levenshtein_sql, 336 exp.Max: max_or_greatest, 337 exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)), 338 exp.MD5Digest: rename_func("MD5"), 339 exp.Min: min_or_least, 340 exp.Normalize: lambda self, e: self.func( 341 "NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE", 342 e.this, 343 e.args.get("form"), 344 ), 345 exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}", 346 exp.RegexpExtract: lambda self, e: self.func( 347 "REGEXP_EXTRACT", 348 e.this, 349 e.expression, 350 e.args.get("position"), 351 e.args.get("occurrence"), 352 ), 353 exp.RegexpExtractAll: lambda self, e: self.func("REGEXP_EXTRACT_ALL", e.this, e.expression), 354 exp.RegexpReplace: regexp_replace_sql, 355 exp.RegexpLike: rename_func("REGEXP_CONTAINS"), 356 exp.ReturnsProperty: _returnsproperty_sql, 357 exp.Rollback: lambda *_: "ROLLBACK TRANSACTION", 358 exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this), 359 exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this), 360 exp.Select: transforms.preprocess( 361 [ 362 transforms.explode_projection_to_unnest(), 363 transforms.unqualify_unnest, 364 transforms.eliminate_distinct_on, 365 _alias_ordered_group, 366 transforms.eliminate_semi_and_anti_joins, 367 ] 368 ), 369 exp.SHA: rename_func("SHA1"), 370 exp.SHA2: sha256_sql, 371 exp.SHA1Digest: rename_func("SHA1"), 372 exp.SHA2Digest: sha2_digest_sql, 373 exp.StabilityProperty: lambda self, e: ( 374 "DETERMINISTIC" if e.name == "IMMUTABLE" else "NOT DETERMINISTIC" 375 ), 376 exp.String: rename_func("STRING"), 377 exp.StrPosition: lambda self, e: strposition_sql( 378 self, e, func_name="INSTR", supports_position=True, supports_occurrence=True 379 ), 380 exp.StrToDate: _str_to_datetime_sql, 381 exp.StrToTime: _str_to_datetime_sql, 382 exp.SessionUser: lambda *_: "SESSION_USER()", 383 exp.TimeAdd: date_add_interval_sql("TIME", "ADD"), 384 exp.TimeFromParts: rename_func("TIME"), 385 exp.TimestampFromParts: rename_func("DATETIME"), 386 exp.TimeSub: date_add_interval_sql("TIME", "SUB"), 387 exp.TimestampAdd: date_add_interval_sql("TIMESTAMP", "ADD"), 388 exp.TimestampDiff: rename_func("TIMESTAMP_DIFF"), 389 exp.TimestampSub: date_add_interval_sql("TIMESTAMP", "SUB"), 390 exp.TimeStrToTime: timestrtotime_sql, 391 exp.Transaction: lambda *_: "BEGIN TRANSACTION", 392 exp.TsOrDsAdd: _ts_or_ds_add_sql, 393 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 394 exp.TsOrDsToTime: rename_func("TIME"), 395 exp.TsOrDsToDatetime: rename_func("DATETIME"), 396 exp.TsOrDsToTimestamp: rename_func("TIMESTAMP"), 397 exp.Unhex: rename_func("FROM_HEX"), 398 exp.UnixDate: rename_func("UNIX_DATE"), 399 exp.UnixToTime: _unix_to_time_sql, 400 exp.Uuid: lambda *_: "GENERATE_UUID()", 401 exp.Values: _derived_table_values_to_unnest, 402 exp.VariancePop: rename_func("VAR_POP"), 403 exp.SafeDivide: rename_func("SAFE_DIVIDE"), 404 } 405 406 SUPPORTED_JSON_PATH_PARTS = { 407 exp.JSONPathKey, 408 exp.JSONPathRoot, 409 exp.JSONPathSubscript, 410 } 411 412 TYPE_MAPPING = { 413 **generator.Generator.TYPE_MAPPING, 414 exp.DType.BIGDECIMAL: "BIGNUMERIC", 415 exp.DType.BIGINT: "INT64", 416 exp.DType.BINARY: "BYTES", 417 exp.DType.BLOB: "BYTES", 418 exp.DType.BOOLEAN: "BOOL", 419 exp.DType.CHAR: "STRING", 420 exp.DType.DECIMAL: "NUMERIC", 421 exp.DType.DOUBLE: "FLOAT64", 422 exp.DType.FLOAT: "FLOAT64", 423 exp.DType.INT: "INT64", 424 exp.DType.NCHAR: "STRING", 425 exp.DType.NVARCHAR: "STRING", 426 exp.DType.SMALLINT: "INT64", 427 exp.DType.TEXT: "STRING", 428 exp.DType.TIMESTAMP: "DATETIME", 429 exp.DType.TIMESTAMPNTZ: "DATETIME", 430 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 431 exp.DType.TIMESTAMPLTZ: "TIMESTAMP", 432 exp.DType.TINYINT: "INT64", 433 exp.DType.ROWVERSION: "BYTES", 434 exp.DType.UUID: "STRING", 435 exp.DType.VARBINARY: "BYTES", 436 exp.DType.VARCHAR: "STRING", 437 exp.DType.VARIANT: "ANY TYPE", 438 } 439 440 PROPERTIES_LOCATION = { 441 **generator.Generator.PROPERTIES_LOCATION, 442 exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 444 } 445 446 # WINDOW comes after QUALIFY 447 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#window_clause 448 # BigQuery requires QUALIFY before WINDOW 449 AFTER_HAVING_MODIFIER_TRANSFORMS = { 450 "qualify": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["qualify"], 451 "windows": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["windows"], 452 } 453 454 # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords 455 RESERVED_KEYWORDS = { 456 "all", 457 "and", 458 "any", 459 "array", 460 "as", 461 "asc", 462 "assert_rows_modified", 463 "at", 464 "between", 465 "by", 466 "case", 467 "cast", 468 "collate", 469 "contains", 470 "create", 471 "cross", 472 "cube", 473 "current", 474 "default", 475 "define", 476 "desc", 477 "distinct", 478 "else", 479 "end", 480 "enum", 481 "escape", 482 "except", 483 "exclude", 484 "exists", 485 "extract", 486 "false", 487 "fetch", 488 "following", 489 "for", 490 "from", 491 "full", 492 "group", 493 "grouping", 494 "groups", 495 "hash", 496 "having", 497 "if", 498 "ignore", 499 "in", 500 "inner", 501 "intersect", 502 "interval", 503 "into", 504 "is", 505 "join", 506 "lateral", 507 "left", 508 "like", 509 "limit", 510 "lookup", 511 "merge", 512 "natural", 513 "new", 514 "no", 515 "not", 516 "null", 517 "nulls", 518 "of", 519 "on", 520 "or", 521 "order", 522 "outer", 523 "over", 524 "partition", 525 "preceding", 526 "proto", 527 "qualify", 528 "range", 529 "recursive", 530 "respect", 531 "right", 532 "rollup", 533 "rows", 534 "select", 535 "set", 536 "some", 537 "struct", 538 "tablesample", 539 "then", 540 "to", 541 "treat", 542 "true", 543 "unbounded", 544 "union", 545 "unnest", 546 "using", 547 "when", 548 "where", 549 "window", 550 "with", 551 "within", 552 } 553 554 def datetrunc_sql(self, expression: exp.DateTrunc) -> str: 555 unit = expression.unit 556 unit_sql = unit.name if unit.is_string else self.sql(unit) 557 return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone")) 558 559 def mod_sql(self, expression: exp.Mod) -> str: 560 this = expression.this 561 expr = expression.expression 562 return self.func( 563 "MOD", 564 this.unnest() if isinstance(this, exp.Paren) else this, 565 expr.unnest() if isinstance(expr, exp.Paren) else expr, 566 ) 567 568 def column_parts(self, expression: exp.Column) -> str: 569 if expression.meta.get("quoted_column"): 570 # If a column reference is of the form `dataset.table`.name, we need 571 # to preserve the quoted table path, otherwise the reference breaks 572 table_parts = ".".join(p.name for p in expression.parts[:-1]) 573 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 574 return f"{table_path}.{self.sql(expression, 'this')}" 575 576 return super().column_parts(expression) 577 578 def table_parts(self, expression: exp.Table) -> str: 579 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 580 # we need to make sure the correct quoting is used in each case. 581 # 582 # For example, if there is a CTE x that clashes with a schema name, then the former will 583 # return the table y in that schema, whereas the latter will return the CTE's y column: 584 # 585 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 586 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 587 if expression.meta.get("quoted_table"): 588 table_parts = ".".join(p.name for p in expression.parts) 589 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 590 591 return super().table_parts(expression) 592 593 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 594 this = expression.this 595 if isinstance(this, exp.TsOrDsToDatetime): 596 func_name = "FORMAT_DATETIME" 597 elif isinstance(this, exp.TsOrDsToTimestamp): 598 func_name = "FORMAT_TIMESTAMP" 599 elif isinstance(this, exp.TsOrDsToTime): 600 func_name = "FORMAT_TIME" 601 else: 602 func_name = "FORMAT_DATE" 603 604 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 605 return self.func( 606 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 607 ) 608 609 def eq_sql(self, expression: exp.EQ) -> str: 610 # Operands of = cannot be NULL in BigQuery 611 if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null): 612 if not isinstance(expression.parent, exp.Update): 613 return "NULL" 614 615 return self.binary(expression, "=") 616 617 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 618 parent = expression.parent 619 620 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 621 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 622 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 623 return self.func( 624 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 625 ) 626 627 return super().attimezone_sql(expression) 628 629 def trycast_sql(self, expression: exp.TryCast) -> str: 630 return self.cast_sql(expression, safe_prefix="SAFE_") 631 632 def bracket_sql(self, expression: exp.Bracket) -> str: 633 this = expression.this 634 expressions = expression.expressions 635 636 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 637 arg = expressions[0] 638 if arg.type is None: 639 from sqlglot.optimizer.annotate_types import annotate_types 640 641 arg = annotate_types(arg, dialect=self.dialect) 642 643 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 644 # BQ doesn't support bracket syntax with string values for structs 645 return f"{self.sql(this)}.{arg.name}" 646 647 expressions_sql = self.expressions(expression, flat=True) 648 offset = expression.args.get("offset") 649 650 if offset == 0: 651 expressions_sql = f"OFFSET({expressions_sql})" 652 elif offset == 1: 653 expressions_sql = f"ORDINAL({expressions_sql})" 654 elif offset is not None: 655 self.unsupported(f"Unsupported array offset: {offset}") 656 657 if expression.args.get("safe"): 658 expressions_sql = f"SAFE_{expressions_sql}" 659 660 return f"{self.sql(this)}[{expressions_sql}]" 661 662 def in_unnest_op(self, expression: exp.Unnest) -> str: 663 return self.sql(expression) 664 665 def version_sql(self, expression: exp.Version) -> str: 666 if expression.name == "TIMESTAMP": 667 expression.set("this", "SYSTEM_TIME") 668 return super().version_sql(expression) 669 670 def contains_sql(self, expression: exp.Contains) -> str: 671 this = expression.this 672 expr = expression.expression 673 674 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 675 this = this.this 676 expr = expr.this 677 678 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope")) 679 680 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 681 this = expression.this 682 683 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 684 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 685 # because they aren't literals and so the above syntax is invalid BigQuery. 686 if isinstance(this, exp.Array): 687 elem = seq_get(this.expressions, 0) 688 if not (elem and elem.find(exp.Query)): 689 return f"{self.sql(expression, 'to')}{self.sql(this)}" 690 691 return super().cast_sql(expression, safe_prefix=safe_prefix)
Generator converts a given syntax tree to the corresponding SQL string.
Arguments:
- pretty: Whether to format the produced SQL string. Default: False.
- identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True: Always quote except for specials cases. 'safe': Only quote identifiers that are case insensitive.
- normalize: Whether to normalize identifiers to lowercase. Default: False.
- pad: The pad size in a formatted string. For example, this affects the indentation of a projection in a query, relative to its nesting level. Default: 2.
- indent: The indentation size in a formatted string. For example, this affects the
indentation of subqueries and filters under a
WHEREclause. Default: 2. - normalize_functions: How to normalize function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
- unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
- max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
- leading_comma: Whether the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
- max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
- comments: Whether to preserve comments in the output SQL code. Default: True
WINDOW_FUNCS_WITH_NULL_ORDERING =
(<class 'sqlglot.expressions.aggregate.CumeDist'>, <class 'sqlglot.expressions.aggregate.DenseRank'>, <class 'sqlglot.expressions.aggregate.FirstValue'>, <class 'sqlglot.expressions.aggregate.Lag'>, <class 'sqlglot.expressions.aggregate.LastValue'>, <class 'sqlglot.expressions.aggregate.Lead'>, <class 'sqlglot.expressions.aggregate.NthValue'>, <class 'sqlglot.expressions.aggregate.Ntile'>, <class 'sqlglot.expressions.aggregate.PercentRank'>, <class 'sqlglot.expressions.aggregate.Rank'>, <class 'sqlglot.expressions.aggregate.RowNumber'>)
TS_OR_DS_TYPES =
(<class 'sqlglot.expressions.temporal.TsOrDsToDatetime'>, <class 'sqlglot.expressions.temporal.TsOrDsToTimestamp'>, <class 'sqlglot.expressions.temporal.TsOrDsToTime'>, <class 'sqlglot.expressions.temporal.TsOrDsToDate'>)
TRANSFORMS =
{<class 'sqlglot.expressions.query.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.core.Adjacent'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.AnalyzeColumns'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.AnalyzeWith'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.ArrayContainsAll'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.ArrayOverlaps'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.AssumeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.math.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.string.ConvertToCharset'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CredentialsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.CurrentCatalog'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.SessionUser'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.constraints.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ApiProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ApplicationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CatalogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ComputeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.DatabaseProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.EmptyProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.EndStatement'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.EnviromentProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.HandlerProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ParameterStyleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Except'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.math.Floor'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Get'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.HybridProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Intersect'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.datatypes.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.Int64'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.json.JSONBContainsAnyTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBContainsAllTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBDeleteAtPath'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONObject'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONObjectAgg'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.MaskingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.NetFunc'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.NetworkProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.ExtendsLeft'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.ExtendsRight'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionedByBucket'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionByTruncate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.PositionalColumn'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.InvisibleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ZeroFillColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Put'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <function _returnsproperty_sql>, <class 'sqlglot.expressions.properties.RowAccessProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.SafeFunc'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SecurityIntegrationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.StabilityProperty'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.query.Stream'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.StreamingTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ddl.SwapTable'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.TableColumn'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.Tags'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.VirtualProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ddl.TriggerExecute'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Union'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.UsingTemplateProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.UsingData'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.temporal.UtcDate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.temporal.UtcTime'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.temporal.UtcTimestamp'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Variadic'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.WithProcedureOptions'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.WithOperator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ForceProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.AIEmbed'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.functions.AIGenerate'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.functions.AISimilarity'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.ApproxTopK'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.ApproxDistinct'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.ArgMax'>: <function arg_max_or_min_no_count.<locals>._arg_max_or_min_sql>, <class 'sqlglot.expressions.aggregate.ArgMin'>: <function arg_max_or_min_no_count.<locals>._arg_max_or_min_sql>, <class 'sqlglot.expressions.array.Array'>: <function inline_array_unless_query>, <class 'sqlglot.expressions.array.ArrayContains'>: <function _array_contains_sql>, <class 'sqlglot.expressions.array.ArrayFilter'>: <function filter_array_using_unnest>, <class 'sqlglot.expressions.array.ArrayRemove'>: <function filter_array_using_unnest>, <class 'sqlglot.expressions.math.BitwiseAndAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseOrAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseXorAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseCount'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.ByteLength'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.functions.Cast'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.properties.CollateProperty'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.ddl.Commit'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.CountIf'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.ddl.Create'>: <function _create_sql>, <class 'sqlglot.expressions.query.CTE'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.DateAdd'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.DateDiff'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateFromParts'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.DateStrToDate'>: <function datestrtodate_sql>, <class 'sqlglot.expressions.temporal.DateSub'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.DatetimeAdd'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.DatetimeSub'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.DateFromUnixDate'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.FromTimeZone'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.array.GenerateSeries'>: <function generate_series_sql.<locals>._generate_series_sql>, <class 'sqlglot.expressions.aggregate.GroupConcat'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.string.Hex'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.query.HexString'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.functions.If'>: <function if_sql.<locals>._if_sql>, <class 'sqlglot.expressions.core.ILike'>: <function no_ilike_sql>, <class 'sqlglot.expressions.core.IntDiv'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.json.JSONBool'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.json.JSONExtract'>: <function _json_extract_sql>, <class 'sqlglot.expressions.json.JSONExtractArray'>: <function _json_extract_sql>, <class 'sqlglot.expressions.json.JSONExtractScalar'>: <function _json_extract_sql>, <class 'sqlglot.expressions.json.JSONFormat'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.json.JSONKeysAtDepth'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.JSONValueArray'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Levenshtein'>: <function _levenshtein_sql>, <class 'sqlglot.expressions.aggregate.Max'>: <function max_or_greatest>, <class 'sqlglot.expressions.string.MD5'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.string.MD5Digest'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.Min'>: <function min_or_least>, <class 'sqlglot.expressions.string.Normalize'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionedByProperty'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.string.RegexpExtract'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.string.RegexpExtractAll'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.string.RegexpReplace'>: <function regexp_replace_sql>, <class 'sqlglot.expressions.core.RegexpLike'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.ddl.Rollback'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.ParseTime'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.ParseDatetime'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.query.Select'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.string.SHA'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA2'>: <function sha256_sql>, <class 'sqlglot.expressions.string.SHA1Digest'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA2Digest'>: <function sha2_digest_sql>, <class 'sqlglot.expressions.string.String'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.StrPosition'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.StrToDate'>: <function _str_to_datetime_sql>, <class 'sqlglot.expressions.temporal.StrToTime'>: <function _str_to_datetime_sql>, <class 'sqlglot.expressions.temporal.TimeAdd'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TimeFromParts'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TimestampFromParts'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TimeSub'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TimestampAdd'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TimestampDiff'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TimestampSub'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TimeStrToTime'>: <function timestrtotime_sql>, <class 'sqlglot.expressions.ddl.Transaction'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TsOrDsAdd'>: <function _ts_or_ds_add_sql>, <class 'sqlglot.expressions.temporal.TsOrDsDiff'>: <function _ts_or_ds_diff_sql>, <class 'sqlglot.expressions.temporal.TsOrDsToTime'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TsOrDsToDatetime'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TsOrDsToTimestamp'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Unhex'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.UnixDate'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.UnixToTime'>: <function _unix_to_time_sql>, <class 'sqlglot.expressions.functions.Uuid'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.query.Values'>: <function _derived_table_values_to_unnest>, <class 'sqlglot.expressions.aggregate.VariancePop'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.SafeDivide'>: <function rename_func.<locals>.<lambda>>}
SUPPORTED_JSON_PATH_PARTS =
{<class 'sqlglot.expressions.query.JSONPathKey'>, <class 'sqlglot.expressions.query.JSONPathSubscript'>, <class 'sqlglot.expressions.query.JSONPathRoot'>}
TYPE_MAPPING =
{<DType.DATETIME2: 'DATETIME2'>: 'TIMESTAMP', <DType.NCHAR: 'NCHAR'>: 'STRING', <DType.NVARCHAR: 'NVARCHAR'>: 'STRING', <DType.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <DType.LONGTEXT: 'LONGTEXT'>: 'TEXT', <DType.TINYTEXT: 'TINYTEXT'>: 'TEXT', <DType.BLOB: 'BLOB'>: 'BYTES', <DType.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <DType.LONGBLOB: 'LONGBLOB'>: 'BLOB', <DType.TINYBLOB: 'TINYBLOB'>: 'BLOB', <DType.INET: 'INET'>: 'INET', <DType.ROWVERSION: 'ROWVERSION'>: 'BYTES', <DType.SMALLDATETIME: 'SMALLDATETIME'>: 'TIMESTAMP', <DType.BIGDECIMAL: 'BIGDECIMAL'>: 'BIGNUMERIC', <DType.BIGINT: 'BIGINT'>: 'INT64', <DType.BINARY: 'BINARY'>: 'BYTES', <DType.BOOLEAN: 'BOOLEAN'>: 'BOOL', <DType.CHAR: 'CHAR'>: 'STRING', <DType.DECIMAL: 'DECIMAL'>: 'NUMERIC', <DType.DOUBLE: 'DOUBLE'>: 'FLOAT64', <DType.FLOAT: 'FLOAT'>: 'FLOAT64', <DType.INT: 'INT'>: 'INT64', <DType.SMALLINT: 'SMALLINT'>: 'INT64', <DType.TEXT: 'TEXT'>: 'STRING', <DType.TIMESTAMP: 'TIMESTAMP'>: 'DATETIME', <DType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>: 'DATETIME', <DType.TIMESTAMPTZ: 'TIMESTAMPTZ'>: 'TIMESTAMP', <DType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>: 'TIMESTAMP', <DType.TINYINT: 'TINYINT'>: 'INT64', <DType.UUID: 'UUID'>: 'STRING', <DType.VARBINARY: 'VARBINARY'>: 'BYTES', <DType.VARCHAR: 'VARCHAR'>: 'STRING', <DType.VARIANT: 'VARIANT'>: 'ANY TYPE'}
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.properties.AllowedValuesProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AlgorithmProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApiProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApplicationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.AutoIncrementProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AutoRefreshProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BackupProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BlockCompressionProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CatalogProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ChecksumProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CollateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ComputeProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.query.Cluster'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ClusteredByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DistributedByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DuplicateKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DataBlocksizeProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.DatabaseProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DataDeletionProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DefinerProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DictRange'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DynamicProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DistKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DistStyleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EmptyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EncodeProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.EngineProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EnviromentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.HandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ParameterStyleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ExecuteAsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ExternalProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.FallbackProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.FileFormatProperty'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.FreespaceProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.GlobalProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.HeapProperty'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.HybridProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.InheritsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IcebergProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.IncludeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.InputModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IsolatedLoadingProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.JournalProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.LanguageProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LikeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LocationProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LockProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LockingProperty'>: <PropertiesLocation.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.properties.LogProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.MaskingProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.MaterializedProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.MergeBlockRatioProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.ModuleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.NetworkProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.NoPrimaryIndexProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.OnProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.OnCommitProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.query.Order'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.OutputModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.PartitionedByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.PartitionedOfProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.constraints.PrimaryKey'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Property'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.RefreshTriggerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RollupProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowAccessProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowFormatProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatDelimitedProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatSerdeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SampleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SchemaCommentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SecureProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SecurityIntegrationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SerdeProperties'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.Set'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SettingsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SetProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SetConfigProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SharingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.SequenceProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.TriggerProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.SortKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlReadWriteProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlSecurityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StabilityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StorageHandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StreamingTableProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.StrictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Tags'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.TemporaryProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ToTableProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.TransientProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.TransformModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.MergeTreeTTL'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.UnloggedProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.UsingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.UsingTemplateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ViewAttributeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.VirtualProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.VolatileProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.WithDataProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.WithJournalTableProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.WithProcedureOptions'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSchemaBindingProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSystemVersioningProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ForceProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>}
RESERVED_KEYWORDS =
{'like', 'right', 'groups', 'nulls', 'tablesample', 'natural', 'with', 'grouping', 'limit', 'join', 'new', 'partition', 'all', 'cross', 'exclude', 'treat', 'desc', 'true', 'array', 'create', 'collate', 'of', 'group', 'asc', 'by', 'between', 'as', 'no', 'having', 'preceding', 'left', 'qualify', 'or', 'outer', 'except', 'interval', 'proto', 'exists', 'fetch', 'some', 'ignore', 'false', 'full', 'into', 'else', 'then', 'null', 'distinct', 'set', 'end', 'to', 'cast', 'union', 'where', 'is', 'default', 'merge', 'hash', 'at', 'lookup', 'from', 'rows', 'struct', 'rollup', 'range', 'unnest', 'escape', 'any', 'assert_rows_modified', 'if', 'recursive', 'window', 'define', 'and', 'inner', 'intersect', 'not', 'extract', 'enum', 'on', 'contains', 'order', 'in', 'over', 'lateral', 'case', 'unbounded', 'when', 'for', 'cube', 'respect', 'within', 'current', 'using', 'following', 'select'}
568 def column_parts(self, expression: exp.Column) -> str: 569 if expression.meta.get("quoted_column"): 570 # If a column reference is of the form `dataset.table`.name, we need 571 # to preserve the quoted table path, otherwise the reference breaks 572 table_parts = ".".join(p.name for p in expression.parts[:-1]) 573 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 574 return f"{table_path}.{self.sql(expression, 'this')}" 575 576 return super().column_parts(expression)
578 def table_parts(self, expression: exp.Table) -> str: 579 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 580 # we need to make sure the correct quoting is used in each case. 581 # 582 # For example, if there is a CTE x that clashes with a schema name, then the former will 583 # return the table y in that schema, whereas the latter will return the CTE's y column: 584 # 585 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 586 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 587 if expression.meta.get("quoted_table"): 588 table_parts = ".".join(p.name for p in expression.parts) 589 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 590 591 return super().table_parts(expression)
593 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 594 this = expression.this 595 if isinstance(this, exp.TsOrDsToDatetime): 596 func_name = "FORMAT_DATETIME" 597 elif isinstance(this, exp.TsOrDsToTimestamp): 598 func_name = "FORMAT_TIMESTAMP" 599 elif isinstance(this, exp.TsOrDsToTime): 600 func_name = "FORMAT_TIME" 601 else: 602 func_name = "FORMAT_DATE" 603 604 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 605 return self.func( 606 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 607 )
617 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 618 parent = expression.parent 619 620 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 621 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 622 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 623 return self.func( 624 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 625 ) 626 627 return super().attimezone_sql(expression)
632 def bracket_sql(self, expression: exp.Bracket) -> str: 633 this = expression.this 634 expressions = expression.expressions 635 636 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 637 arg = expressions[0] 638 if arg.type is None: 639 from sqlglot.optimizer.annotate_types import annotate_types 640 641 arg = annotate_types(arg, dialect=self.dialect) 642 643 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 644 # BQ doesn't support bracket syntax with string values for structs 645 return f"{self.sql(this)}.{arg.name}" 646 647 expressions_sql = self.expressions(expression, flat=True) 648 offset = expression.args.get("offset") 649 650 if offset == 0: 651 expressions_sql = f"OFFSET({expressions_sql})" 652 elif offset == 1: 653 expressions_sql = f"ORDINAL({expressions_sql})" 654 elif offset is not None: 655 self.unsupported(f"Unsupported array offset: {offset}") 656 657 if expression.args.get("safe"): 658 expressions_sql = f"SAFE_{expressions_sql}" 659 660 return f"{self.sql(this)}[{expressions_sql}]"
670 def contains_sql(self, expression: exp.Contains) -> str: 671 this = expression.this 672 expr = expression.expression 673 674 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 675 this = this.this 676 expr = expr.this 677 678 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope"))
def
cast_sql( self, expression: sqlglot.expressions.functions.Cast, safe_prefix: str | None = None) -> str:
680 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 681 this = expression.this 682 683 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 684 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 685 # because they aren't literals and so the above syntax is invalid BigQuery. 686 if isinstance(this, exp.Array): 687 elem = seq_get(this.expressions, 0) 688 if not (elem and elem.find(exp.Query)): 689 return f"{self.sql(expression, 'to')}{self.sql(this)}" 690 691 return super().cast_sql(expression, safe_prefix=safe_prefix)
Inherited Members
- sqlglot.generator.Generator
- Generator
- IGNORE_NULLS_BEFORE_ORDER
- LOCKING_READS_SUPPORTED
- WRAP_DERIVED_VALUES
- CREATE_FUNCTION_RETURN_AS
- MATCHED_BY_SOURCE
- SUPPORTS_MERGE_WHERE
- SINGLE_STRING_INTERVAL
- GROUPINGS_SEP
- INDEX_ON
- INOUT_SEPARATOR
- DIRECTED_JOINS
- QUERY_HINT_SEP
- IS_BOOL_ALLOWED
- DUPLICATE_KEY_UPDATE_WITH_SET
- LIMIT_IS_TOP
- RETURNING_END
- EXTRACT_ALLOWS_QUOTES
- TZ_TO_WITH_TIME_ZONE
- SELECT_KINDS
- VALUES_AS_TABLE
- ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
- AGGREGATE_FILTER_SUPPORTED
- SEMI_ANTI_JOIN_WITH_SIDE
- COMPUTED_COLUMN_WITH_TYPE
- SUPPORTS_TABLE_COPY
- TABLESAMPLE_REQUIRES_PARENS
- TABLESAMPLE_SIZE_IS_ROWS
- TABLESAMPLE_KEYWORDS
- TABLESAMPLE_WITH_METHOD
- TABLESAMPLE_SEED_KEYWORD
- DATA_TYPE_SPECIFIERS_ALLOWED
- ENSURE_BOOLS
- CTE_RECURSIVE_KEYWORD_REQUIRED
- SUPPORTS_SINGLE_ARG_CONCAT
- LAST_DAY_SUPPORTS_DATE_PART
- INSERT_OVERWRITE
- SUPPORTS_SELECT_INTO
- SUPPORTS_UNLOGGED_TABLES
- SUPPORTS_CREATE_TABLE_LIKE
- SUPPORTS_MODIFY_COLUMN
- SUPPORTS_CHANGE_COLUMN
- LIKE_PROPERTY_INSIDE_SCHEMA
- MULTI_ARG_DISTINCT
- JSON_TYPE_REQUIRED_FOR_EXTRACTION
- JSON_PATH_BRACKETED_KEY_SUPPORTED
- SUPPORTS_WINDOW_EXCLUDE
- SET_OP_MODIFIERS
- COPY_PARAMS_ARE_WRAPPED
- COPY_PARAMS_EQ_REQUIRED
- COPY_HAS_INTO_KEYWORD
- UNICODE_SUBSTITUTE
- STAR_EXCEPT
- QUOTE_JSON_PATH
- PAD_FILL_PATTERN_IS_REQUIRED
- ARRAY_CONCAT_IS_VAR_LEN
- SUPPORTS_CONVERT_TIMEZONE
- SUPPORTS_MEDIAN
- ALTER_SET_WRAPPED
- NORMALIZE_EXTRACT_DATE_PARTS
- PARSE_JSON_NAME
- ARRAY_SIZE_NAME
- ALTER_SET_TYPE
- ARRAY_SIZE_DIM_REQUIRED
- SUPPORTS_BETWEEN_FLAGS
- SUPPORTS_LIKE_QUANTIFIERS
- MATCH_AGAINST_TABLE_PREFIX
- SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD
- UPDATE_STATEMENT_SUPPORTS_FROM
- STAR_EXCLUDE_REQUIRES_DERIVED_TABLE
- SUPPORTS_DROP_ALTER_ICEBERG_PROPERTY
- UNSUPPORTED_TYPES
- TYPE_PARAM_SETTINGS
- TIME_PART_SINGULARS
- TOKEN_MAPPING
- STRUCT_DELIMITER
- PARAMETER_TOKEN
- EXPRESSION_PRECEDES_PROPERTIES_CREATABLES
- WITH_SEPARATED_COMMENTS
- EXCLUDE_COMMENTS
- UNWRAPPED_INTERVAL_VALUES
- PARAMETERIZABLE_TEXT_TYPES
- EXPRESSIONS_WITHOUT_NESTED_CTES
- RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS
- SENTINEL_LINE_BREAK
- pretty
- identify
- normalize
- pad
- unsupported_level
- max_unsupported
- leading_comma
- max_text_width
- comments
- dialect
- normalize_functions
- unsupported_messages
- generate
- preprocess
- unsupported
- sep
- seg
- sanitize_comment
- maybe_comment
- wrap
- no_identify
- normalize_func
- indent
- sql
- uncache_sql
- cache_sql
- characterset_sql
- column_sql
- pseudocolumn_sql
- columnposition_sql
- columndef_sql
- columnconstraint_sql
- computedcolumnconstraint_sql
- autoincrementcolumnconstraint_sql
- compresscolumnconstraint_sql
- generatedasidentitycolumnconstraint_sql
- generatedasrowcolumnconstraint_sql
- periodforsystemtimeconstraint_sql
- notnullcolumnconstraint_sql
- primarykeycolumnconstraint_sql
- uniquecolumnconstraint_sql
- inoutcolumnconstraint_sql
- createable_sql
- create_sql
- sequenceproperties_sql
- triggerproperties_sql
- triggerreferencing_sql
- triggerevent_sql
- clone_sql
- describe_sql
- heredoc_sql
- prepend_ctes
- with_sql
- cte_sql
- tablealias_sql
- bitstring_sql
- hexstring_sql
- bytestring_sql
- unicodestring_sql
- rawstring_sql
- datatypeparam_sql
- datatype_param_bound_limiter
- datatype_sql
- directory_sql
- delete_sql
- drop_sql
- set_operation
- set_operations
- fetch_sql
- limitoptions_sql
- filter_sql
- hint_sql
- indexparameters_sql
- index_sql
- identifier_sql
- hex_sql
- lowerhex_sql
- inputoutputformat_sql
- national_sql
- partition_sql
- properties_sql
- root_properties
- properties
- with_properties
- locate_properties
- property_name
- property_sql
- uuidproperty_sql
- likeproperty_sql
- fallbackproperty_sql
- journalproperty_sql
- freespaceproperty_sql
- checksumproperty_sql
- mergeblockratioproperty_sql
- moduleproperty_sql
- datablocksizeproperty_sql
- blockcompressionproperty_sql
- isolatedloadingproperty_sql
- partitionboundspec_sql
- partitionedofproperty_sql
- lockingproperty_sql
- withdataproperty_sql
- withsystemversioningproperty_sql
- insert_sql
- introducer_sql
- kill_sql
- pseudotype_sql
- objectidentifier_sql
- onconflict_sql
- returning_sql
- rowformatdelimitedproperty_sql
- withtablehint_sql
- indextablehint_sql
- historicaldata_sql
- table_sql
- tablefromrows_sql
- tablesample_sql
- pivot_sql
- tuple_sql
- update_sql
- values_sql
- var_sql
- into_sql
- from_sql
- groupingsets_sql
- rollup_sql
- rollupindex_sql
- rollupproperty_sql
- cube_sql
- group_sql
- having_sql
- connect_sql
- prior_sql
- join_sql
- lambda_sql
- lateral_op
- lateral_sql
- limit_sql
- offset_sql
- setitem_sql
- set_sql
- queryband_sql
- pragma_sql
- lock_sql
- literal_sql
- escape_str
- loaddata_sql
- null_sql
- boolean_sql
- booland_sql
- boolor_sql
- order_sql
- withfill_sql
- cluster_sql
- distribute_sql
- sort_sql
- ordered_sql
- matchrecognizemeasure_sql
- matchrecognize_sql
- query_modifiers
- options_modifier
- for_modifiers
- queryoption_sql
- offset_limit_modifiers
- after_limit_modifiers
- select_sql
- schema_sql
- schema_columns_sql
- star_sql
- parameter_sql
- sessionparameter_sql
- placeholder_sql
- subquery_sql
- qualify_sql
- unnest_sql
- prewhere_sql
- where_sql
- window_sql
- partition_by_sql
- windowspec_sql
- withingroup_sql
- between_sql
- bracket_offset_expressions
- all_sql
- any_sql
- exists_sql
- case_sql
- constraint_sql
- nextvaluefor_sql
- extract_sql
- trim_sql
- convert_concat_args
- concat_sql
- concatws_sql
- check_sql
- foreignkey_sql
- primarykey_sql
- if_sql
- matchagainst_sql
- jsonkeyvalue_sql
- jsonpath_sql
- json_path_part
- formatjson_sql
- formatphrase_sql
- jsonarray_sql
- jsonarrayagg_sql
- jsoncolumndef_sql
- jsonschema_sql
- jsontable_sql
- openjsoncolumndef_sql
- openjson_sql
- in_sql
- interval_sql
- return_sql
- reference_sql
- anonymous_sql
- paren_sql
- neg_sql
- not_sql
- alias_sql
- pivotalias_sql
- aliases_sql
- atindex_sql
- fromtimezone_sql
- add_sql
- and_sql
- or_sql
- xor_sql
- connector_sql
- bitwiseand_sql
- bitwiseleftshift_sql
- bitwisenot_sql
- bitwiseor_sql
- bitwiserightshift_sql
- bitwisexor_sql
- strtotime_sql
- currentdate_sql
- collate_sql
- command_sql
- comment_sql
- mergetreettlaction_sql
- mergetreettl_sql
- transaction_sql
- commit_sql
- rollback_sql
- altercolumn_sql
- modifycolumn_sql
- alterindex_sql
- alterdiststyle_sql
- altersortkey_sql
- alterrename_sql
- renamecolumn_sql
- alterset_sql
- alter_sql
- altersession_sql
- add_column_sql
- droppartition_sql
- dropprimarykey_sql
- addconstraint_sql
- addpartition_sql
- distinct_sql
- ignorenulls_sql
- respectnulls_sql
- havingmax_sql
- intdiv_sql
- dpipe_sql
- div_sql
- safedivide_sql
- overlaps_sql
- distance_sql
- dot_sql
- propertyeq_sql
- escape_sql
- glob_sql
- gt_sql
- gte_sql
- is_sql
- like_sql
- ilike_sql
- match_sql
- similarto_sql
- lt_sql
- lte_sql
- mul_sql
- neq_sql
- nullsafeeq_sql
- nullsafeneq_sql
- sub_sql
- jsoncast_sql
- try_sql
- log_sql
- use_sql
- binary
- ceil_floor
- function_fallback_sql
- func
- format_args
- too_wide
- format_time
- expressions
- op_expressions
- naked_property
- tag_sql
- token_sql
- userdefinedfunction_sql
- joinhint_sql
- kwarg_sql
- when_sql
- whens_sql
- merge_sql
- tochar_sql
- tonumber_sql
- dictproperty_sql
- dictrange_sql
- dictsubproperty_sql
- duplicatekeyproperty_sql
- uniquekeyproperty_sql
- distributedbyproperty_sql
- oncluster_sql
- clusteredbyproperty_sql
- anyvalue_sql
- querytransform_sql
- indexconstraintoption_sql
- checkcolumnconstraint_sql
- indexcolumnconstraint_sql
- nvl2_sql
- comprehension_sql
- columnprefix_sql
- opclass_sql
- predict_sql
- generateembedding_sql
- generatetext_sql
- generatetable_sql
- generatebool_sql
- generateint_sql
- generatedouble_sql
- mltranslate_sql
- mlforecast_sql
- aiforecast_sql
- featuresattime_sql
- vectorsearch_sql
- forin_sql
- refresh_sql
- toarray_sql
- tsordstotime_sql
- tsordstotimestamp_sql
- tsordstodatetime_sql
- tsordstodate_sql
- unixdate_sql
- lastday_sql
- dateadd_sql
- arrayany_sql
- struct_sql
- partitionrange_sql
- truncatetable_sql
- convert_sql
- copyparameter_sql
- credentials_sql
- copy_sql
- semicolon_sql
- datadeletionproperty_sql
- maskingpolicycolumnconstraint_sql
- gapfill_sql
- scope_resolution
- scoperesolution_sql
- parsejson_sql
- rand_sql
- changes_sql
- pad_sql
- summarize_sql
- explodinggenerateseries_sql
- converttimezone_sql
- json_sql
- jsonvalue_sql
- skipjsoncolumn_sql
- conditionalinsert_sql
- multitableinserts_sql
- oncondition_sql
- jsonextractquote_sql
- jsonexists_sql
- arrayagg_sql
- slice_sql
- apply_sql
- grant_sql
- revoke_sql
- grantprivilege_sql
- grantprincipal_sql
- columns_sql
- overlay_sql
- todouble_sql
- string_sql
- median_sql
- overflowtruncatebehavior_sql
- unixseconds_sql
- arraysize_sql
- attach_sql
- detach_sql
- attachoption_sql
- watermarkcolumnconstraint_sql
- encodeproperty_sql
- includeproperty_sql
- xmlelement_sql
- xmlkeyvalueoption_sql
- partitionbyrangeproperty_sql
- partitionbyrangepropertydynamic_sql
- unpivotcolumns_sql
- analyzesample_sql
- analyzestatistics_sql
- analyzehistogram_sql
- analyzedelete_sql
- analyzelistchainedrows_sql
- analyzevalidate_sql
- analyze_sql
- xmltable_sql
- xmlnamespace_sql
- export_sql
- declare_sql
- declareitem_sql
- recursivewithsearch_sql
- parameterizedagg_sql
- anonymousaggfunc_sql
- combinedaggfunc_sql
- combinedparameterizedagg_sql
- show_sql
- install_sql
- get_put_sql
- translatecharacters_sql
- decodecase_sql
- semanticview_sql
- getextract_sql
- datefromunixdate_sql
- space_sql
- buildproperty_sql
- refreshtriggerproperty_sql
- modelattribute_sql
- directorystage_sql
- uuid_sql
- initcap_sql
- localtime_sql
- localtimestamp_sql
- weekstart_sql
- chr_sql
- block_sql
- storedprocedure_sql
- ifblock_sql
- whileblock_sql
- execute_sql
- executesql_sql
- altermodifysqlsecurity_sql
- usingproperty_sql
- renameindex_sql