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 _unnest_explode_generate_series(expression: exp.Expr) -> exp.Expr: 141 """ 142 Rewrites exploding GENERATE_SERIES projections into table references, e.g. 143 144 SELECT GENERATE_SERIES(1, 2) AS x -> SELECT x FROM GENERATE_SERIES(1, 2) AS x 145 SELECT y, GENERATE_SERIES(1, 2) AS x FROM t -> SELECT y, x FROM t CROSS JOIN GENERATE_SERIES(1, 2) AS x 146 147 since BigQuery can't explode in the projection and must unnest it in the FROM clause instead. 148 The resulting table reference is unnested downstream by `transforms.unnest_generate_series`. 149 """ 150 if isinstance(expression, exp.Select): 151 for projection in expression.selects: 152 if isinstance(series := projection.unalias(), exp.ExplodingGenerateSeries): 153 column_name = projection.output_name or "_gen_series_value" 154 155 projection.replace(exp.column(column_name)) 156 table = exp.Table( 157 this=series, alias=exp.TableAlias(this=exp.to_identifier(column_name)) 158 ) 159 160 if expression.args.get("from_"): 161 expression.join(table, copy=False, join_type="CROSS") 162 else: 163 expression.set("from_", exp.From(this=table)) 164 165 return expression 166 167 168def _array_contains_sql(self: BigQueryGenerator, expression: exp.ArrayContains) -> str: 169 return self.sql( 170 exp.Exists( 171 this=exp.select("1") 172 .from_(exp.Unnest(expressions=[expression.left]).as_("_unnest", table=["_col"])) 173 .where(exp.column("_col").eq(expression.right)) 174 ) 175 ) 176 177 178def _ts_or_ds_add_sql(self: BigQueryGenerator, expression: exp.TsOrDsAdd) -> str: 179 return date_add_interval_sql("DATE", "ADD")(self, ts_or_ds_add_cast(expression)) 180 181 182def _ts_or_ds_diff_sql(self: BigQueryGenerator, expression: exp.TsOrDsDiff) -> str: 183 expression.this.replace(exp.cast(expression.this, exp.DType.TIMESTAMP)) 184 expression.expression.replace(exp.cast(expression.expression, exp.DType.TIMESTAMP)) 185 unit = unit_to_var(expression) 186 return self.func("DATE_DIFF", expression.this, expression.expression, unit) 187 188 189def _unix_to_time_sql(self: BigQueryGenerator, expression: exp.UnixToTime) -> str: 190 scale = expression.args.get("scale") 191 timestamp = expression.this 192 193 if scale in (None, exp.UnixToTime.SECONDS): 194 return self.func("TIMESTAMP_SECONDS", timestamp) 195 if scale == exp.UnixToTime.MILLIS: 196 return self.func("TIMESTAMP_MILLIS", timestamp) 197 if scale == exp.UnixToTime.MICROS: 198 return self.func("TIMESTAMP_MICROS", timestamp) 199 200 unix_seconds = exp.cast( 201 exp.Div(this=timestamp, expression=exp.func("POW", 10, scale)), exp.DType.BIGINT 202 ) 203 return self.func("TIMESTAMP_SECONDS", unix_seconds) 204 205 206def _str_to_datetime_sql(self: BigQueryGenerator, expression: exp.StrToDate | exp.StrToTime) -> str: 207 this = self.sql(expression, "this") 208 dtype = "DATE" if isinstance(expression, exp.StrToDate) else "TIMESTAMP" 209 210 if expression.args.get("safe"): 211 fmt = self.format_time( 212 expression, 213 self.dialect.INVERSE_FORMAT_MAPPING, 214 self.dialect.INVERSE_FORMAT_TRIE, 215 ) 216 return f"SAFE_CAST({this} AS {dtype} FORMAT {fmt})" 217 218 fmt = self.format_time(expression) 219 return self.func(f"PARSE_{dtype}", fmt, this, expression.args.get("zone")) 220 221 222@unsupported_args("ins_cost", "del_cost", "sub_cost") 223def _levenshtein_sql(self: BigQueryGenerator, expression: exp.Levenshtein) -> str: 224 max_dist = expression.args.get("max_dist") 225 if max_dist: 226 max_dist = exp.Kwarg(this=exp.var("max_distance"), expression=max_dist) 227 228 return self.func("EDIT_DISTANCE", expression.this, expression.expression, max_dist) 229 230 231def _json_extract_sql(self: BigQueryGenerator, expression: JSON_EXTRACT_TYPE) -> str: 232 name = expression.meta_get("name") or expression.sql_name() 233 upper = name.upper() 234 235 dquote_escaping = upper in DQUOTES_ESCAPING_JSON_FUNCTIONS 236 237 if dquote_escaping: 238 self._quote_json_path_key_using_brackets = False 239 240 sql = rename_func(upper)(self, expression) 241 242 if dquote_escaping: 243 self._quote_json_path_key_using_brackets = True 244 245 return sql 246 247 248class BigQueryGenerator(generator.Generator): 249 TRY_SUPPORTED = False 250 SUPPORTS_UESCAPE = False 251 SUPPORTS_DECODE_CASE = False 252 INTERVAL_ALLOWS_PLURAL_FORM = False 253 JOIN_HINTS = False 254 QUERY_HINTS = False 255 TABLE_HINTS = False 256 LIMIT_FETCH = "LIMIT" 257 RENAME_TABLE_WITH_DB = False 258 NVL2_SUPPORTED = False 259 UNNEST_WITH_ORDINALITY = False 260 COLLATE_IS_FUNC = True 261 LIMIT_ONLY_LITERALS = True 262 SUPPORTS_TABLE_ALIAS_COLUMNS = False 263 SUPPORTS_NAMED_CTE_COLUMNS = False 264 UNPIVOT_ALIASES_ARE_IDENTIFIERS = False 265 JSON_KEY_VALUE_PAIR_SEP = "," 266 NULL_ORDERING_SUPPORTED: bool | None = False 267 IGNORE_NULLS_IN_FUNC = True 268 JSON_PATH_SINGLE_QUOTE_ESCAPE = True 269 CAN_IMPLEMENT_ARRAY_ANY = True 270 SUPPORTS_TO_NUMBER = False 271 NAMED_PLACEHOLDER_TOKEN = "@" 272 HEX_FUNC = "TO_HEX" 273 WITH_PROPERTIES_PREFIX = "OPTIONS" 274 SUPPORTS_EXPLODING_PROJECTIONS = False 275 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 276 SUPPORTS_UNIX_SECONDS = True 277 DECLARE_DEFAULT_ASSIGNMENT = "DEFAULT" 278 279 SAFE_JSON_PATH_KEY_RE = re.compile(r"^[\-\w]*$") 280 281 WINDOW_FUNCS_WITH_NULL_ORDERING = ( 282 exp.CumeDist, 283 exp.DenseRank, 284 exp.FirstValue, 285 exp.Lag, 286 exp.LastValue, 287 exp.Lead, 288 exp.NthValue, 289 exp.Ntile, 290 exp.PercentRank, 291 exp.Rank, 292 exp.RowNumber, 293 ) 294 295 TS_OR_DS_TYPES = ( 296 exp.TsOrDsToDatetime, 297 exp.TsOrDsToTimestamp, 298 exp.TsOrDsToTime, 299 exp.TsOrDsToDate, 300 ) 301 302 TRANSFORMS = { 303 **generator.Generator.TRANSFORMS, 304 exp.AIEmbed: rename_func("EMBED"), 305 exp.AIGenerate: rename_func("GENERATE"), 306 exp.AISimilarity: rename_func("SIMILARITY"), 307 exp.ApproxTopK: rename_func("APPROX_TOP_COUNT"), 308 exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"), 309 exp.ArgMax: arg_max_or_min_no_count("MAX_BY"), 310 exp.ArgMin: arg_max_or_min_no_count("MIN_BY"), 311 exp.Array: inline_array_unless_query, 312 exp.ArrayContains: _array_contains_sql, 313 exp.ArrayFilter: filter_array_using_unnest, 314 exp.ArrayRemove: filter_array_using_unnest, 315 exp.BitwiseAndAgg: rename_func("BIT_AND"), 316 exp.BitwiseOrAgg: rename_func("BIT_OR"), 317 exp.BitwiseXorAgg: rename_func("BIT_XOR"), 318 exp.BitwiseCount: rename_func("BIT_COUNT"), 319 exp.ByteLength: rename_func("BYTE_LENGTH"), 320 exp.Cast: transforms.preprocess([transforms.remove_precision_parameterized_types]), 321 exp.CollateProperty: lambda self, e: ( 322 f"DEFAULT COLLATE {self.sql(e, 'this')}" 323 if e.args.get("default") 324 else f"COLLATE {self.sql(e, 'this')}" 325 ), 326 exp.Commit: lambda *_: "COMMIT TRANSACTION", 327 exp.CountIf: rename_func("COUNTIF"), 328 exp.Create: _create_sql, 329 exp.CTE: transforms.preprocess([_pushdown_cte_column_names]), 330 exp.DateAdd: date_add_interval_sql("DATE", "ADD"), 331 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", e.this, e.expression, unit_to_var(e)), 332 exp.DateFromParts: rename_func("DATE"), 333 exp.DateStrToDate: datestrtodate_sql, 334 exp.DateSub: date_add_interval_sql("DATE", "SUB"), 335 exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"), 336 exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"), 337 exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"), 338 exp.FromTimeZone: lambda self, e: self.func( 339 "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'" 340 ), 341 exp.GenerateSeries: generate_series_sql("GENERATE_ARRAY"), 342 exp.GroupConcat: lambda self, e: groupconcat_sql( 343 self, e, func_name="STRING_AGG", within_group=False, sep=None 344 ), 345 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 346 exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"), 347 exp.If: if_sql(false_value="NULL"), 348 exp.ILike: no_ilike_sql, 349 exp.IntDiv: rename_func("DIV"), 350 exp.Int64: rename_func("INT64"), 351 exp.JSONBool: rename_func("BOOL"), 352 exp.JSONExtract: _json_extract_sql, 353 exp.JSONExtractArray: _json_extract_sql, 354 exp.JSONExtractScalar: _json_extract_sql, 355 exp.JSONFormat: lambda self, e: self.func( 356 "TO_JSON" if e.args.get("to_json") else "TO_JSON_STRING", 357 e.this, 358 e.args.get("options"), 359 ), 360 exp.JSONKeysAtDepth: rename_func("JSON_KEYS"), 361 exp.JSONValueArray: rename_func("JSON_VALUE_ARRAY"), 362 exp.Levenshtein: _levenshtein_sql, 363 exp.Max: max_or_greatest, 364 exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)), 365 exp.MD5Digest: rename_func("MD5"), 366 exp.Min: min_or_least, 367 exp.Normalize: lambda self, e: self.func( 368 "NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE", 369 e.this, 370 e.args.get("form"), 371 ), 372 exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}", 373 exp.RegexpExtract: lambda self, e: self.func( 374 "REGEXP_EXTRACT", 375 e.this, 376 e.expression, 377 e.args.get("position"), 378 e.args.get("occurrence"), 379 ), 380 exp.RegexpExtractAll: lambda self, e: self.func("REGEXP_EXTRACT_ALL", e.this, e.expression), 381 exp.RegexpReplace: regexp_replace_sql, 382 exp.RegexpLike: rename_func("REGEXP_CONTAINS"), 383 exp.ReturnsProperty: _returnsproperty_sql, 384 exp.Rollback: lambda *_: "ROLLBACK TRANSACTION", 385 exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this), 386 exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this), 387 exp.Select: transforms.preprocess( 388 [ 389 _unnest_explode_generate_series, 390 transforms.explode_projection_to_unnest(), 391 transforms.unqualify_unnest, 392 transforms.eliminate_distinct_on, 393 _alias_ordered_group, 394 transforms.eliminate_semi_and_anti_joins, 395 ] 396 ), 397 exp.SHA: rename_func("SHA1"), 398 exp.SHA2: sha256_sql, 399 exp.SHA1Digest: rename_func("SHA1"), 400 exp.SHA2Digest: sha2_digest_sql, 401 exp.StabilityProperty: lambda self, e: ( 402 "DETERMINISTIC" if e.name == "IMMUTABLE" else "NOT DETERMINISTIC" 403 ), 404 exp.String: rename_func("STRING"), 405 exp.StrPosition: lambda self, e: strposition_sql( 406 self, e, func_name="INSTR", supports_position=True, supports_occurrence=True 407 ), 408 exp.StrToDate: _str_to_datetime_sql, 409 exp.StrToTime: _str_to_datetime_sql, 410 exp.SessionUser: lambda *_: "SESSION_USER()", 411 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 412 exp.TimeAdd: date_add_interval_sql("TIME", "ADD"), 413 exp.TimeFromParts: rename_func("TIME"), 414 exp.TimestampFromParts: rename_func("DATETIME"), 415 exp.TimeSub: date_add_interval_sql("TIME", "SUB"), 416 exp.TimestampAdd: date_add_interval_sql("TIMESTAMP", "ADD"), 417 exp.TimestampDiff: rename_func("TIMESTAMP_DIFF"), 418 exp.TimestampSub: date_add_interval_sql("TIMESTAMP", "SUB"), 419 exp.TimeStrToTime: timestrtotime_sql, 420 exp.Transaction: lambda *_: "BEGIN TRANSACTION", 421 exp.TsOrDsAdd: _ts_or_ds_add_sql, 422 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 423 exp.TsOrDsToTime: rename_func("TIME"), 424 exp.TsOrDsToDatetime: rename_func("DATETIME"), 425 exp.TsOrDsToTimestamp: rename_func("TIMESTAMP"), 426 exp.Unhex: rename_func("FROM_HEX"), 427 exp.UnixDate: rename_func("UNIX_DATE"), 428 exp.UnixToTime: _unix_to_time_sql, 429 exp.Uuid: lambda *_: "GENERATE_UUID()", 430 exp.Values: _derived_table_values_to_unnest, 431 exp.VariancePop: rename_func("VAR_POP"), 432 exp.SafeDivide: rename_func("SAFE_DIVIDE"), 433 } 434 435 SUPPORTED_JSON_PATH_PARTS = { 436 exp.JSONPathKey, 437 exp.JSONPathRoot, 438 exp.JSONPathSubscript, 439 } 440 441 TYPE_MAPPING = { 442 **generator.Generator.TYPE_MAPPING, 443 exp.DType.BIGDECIMAL: "BIGNUMERIC", 444 exp.DType.BIGINT: "INT64", 445 exp.DType.BINARY: "BYTES", 446 exp.DType.BLOB: "BYTES", 447 exp.DType.BOOLEAN: "BOOL", 448 exp.DType.CHAR: "STRING", 449 exp.DType.DECIMAL: "NUMERIC", 450 exp.DType.DOUBLE: "FLOAT64", 451 exp.DType.FLOAT: "FLOAT64", 452 exp.DType.INT: "INT64", 453 exp.DType.NCHAR: "STRING", 454 exp.DType.NVARCHAR: "STRING", 455 exp.DType.SMALLINT: "INT64", 456 exp.DType.TEXT: "STRING", 457 exp.DType.TIMESTAMP: "DATETIME", 458 exp.DType.TIMESTAMPNTZ: "DATETIME", 459 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 460 exp.DType.TIMESTAMPLTZ: "TIMESTAMP", 461 exp.DType.TINYINT: "INT64", 462 exp.DType.ROWVERSION: "BYTES", 463 exp.DType.UUID: "STRING", 464 exp.DType.VARBINARY: "BYTES", 465 exp.DType.VARCHAR: "STRING", 466 exp.DType.VARIANT: "ANY TYPE", 467 } 468 469 PROPERTIES_LOCATION = { 470 **generator.Generator.PROPERTIES_LOCATION, 471 exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, 472 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 473 } 474 475 # WINDOW comes after QUALIFY 476 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#window_clause 477 # BigQuery requires QUALIFY before WINDOW 478 AFTER_HAVING_MODIFIER_TRANSFORMS = { 479 "qualify": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["qualify"], 480 "windows": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["windows"], 481 } 482 483 # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords 484 RESERVED_KEYWORDS = { 485 "all", 486 "and", 487 "any", 488 "array", 489 "as", 490 "asc", 491 "assert_rows_modified", 492 "at", 493 "between", 494 "by", 495 "case", 496 "cast", 497 "collate", 498 "contains", 499 "create", 500 "cross", 501 "cube", 502 "current", 503 "default", 504 "define", 505 "desc", 506 "distinct", 507 "else", 508 "end", 509 "enum", 510 "escape", 511 "except", 512 "exclude", 513 "exists", 514 "extract", 515 "false", 516 "fetch", 517 "following", 518 "for", 519 "from", 520 "full", 521 "group", 522 "grouping", 523 "groups", 524 "hash", 525 "having", 526 "if", 527 "ignore", 528 "in", 529 "inner", 530 "intersect", 531 "interval", 532 "into", 533 "is", 534 "join", 535 "lateral", 536 "left", 537 "like", 538 "limit", 539 "lookup", 540 "merge", 541 "natural", 542 "new", 543 "no", 544 "not", 545 "null", 546 "nulls", 547 "of", 548 "on", 549 "or", 550 "order", 551 "outer", 552 "over", 553 "partition", 554 "preceding", 555 "proto", 556 "qualify", 557 "range", 558 "recursive", 559 "respect", 560 "right", 561 "rollup", 562 "rows", 563 "select", 564 "set", 565 "some", 566 "struct", 567 "tablesample", 568 "then", 569 "to", 570 "treat", 571 "true", 572 "unbounded", 573 "union", 574 "unnest", 575 "using", 576 "when", 577 "where", 578 "window", 579 "with", 580 "within", 581 } 582 583 def weekstart_sql(self, expression: exp.WeekStart) -> str: 584 if expression.this.name.upper() == "SUNDAY": 585 # BigQuery specific optimization since WEEK(SUNDAY) == WEEK 586 return "WEEK" 587 588 return self.func("WEEK", expression.this) 589 590 def datetrunc_sql(self, expression: exp.DateTrunc) -> str: 591 unit = expression.unit 592 unit_sql = unit.name if unit.is_string else self.sql(unit) 593 return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone")) 594 595 def mod_sql(self, expression: exp.Mod) -> str: 596 this = expression.this 597 expr = expression.expression 598 return self.func( 599 "MOD", 600 this.unnest() if isinstance(this, exp.Paren) else this, 601 expr.unnest() if isinstance(expr, exp.Paren) else expr, 602 ) 603 604 def column_parts(self, expression: exp.Column) -> str: 605 if expression.meta_get("quoted_column"): 606 # If a column reference is of the form `dataset.table`.name, we need 607 # to preserve the quoted table path, otherwise the reference breaks 608 table_parts = ".".join(p.name for p in expression.parts[:-1]) 609 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 610 return f"{table_path}.{self.sql(expression, 'this')}" 611 612 return super().column_parts(expression) 613 614 def table_parts(self, expression: exp.Table) -> str: 615 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 616 # we need to make sure the correct quoting is used in each case. 617 # 618 # For example, if there is a CTE x that clashes with a schema name, then the former will 619 # return the table y in that schema, whereas the latter will return the CTE's y column: 620 # 621 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 622 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 623 if expression.meta_get("quoted_table"): 624 table_parts = ".".join(p.name for p in expression.parts) 625 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 626 627 return super().table_parts(expression) 628 629 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 630 this = expression.this 631 if isinstance(this, exp.TsOrDsToDatetime): 632 func_name = "FORMAT_DATETIME" 633 elif isinstance(this, exp.TsOrDsToTimestamp): 634 func_name = "FORMAT_TIMESTAMP" 635 elif isinstance(this, exp.TsOrDsToTime): 636 func_name = "FORMAT_TIME" 637 else: 638 func_name = "FORMAT_DATE" 639 640 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 641 return self.func( 642 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 643 ) 644 645 def eq_sql(self, expression: exp.EQ) -> str: 646 # Operands of = cannot be NULL in BigQuery 647 if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null): 648 if not isinstance(expression.parent, exp.Update): 649 return "NULL" 650 651 return self.binary(expression, "=") 652 653 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 654 parent = expression.parent 655 656 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 657 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 658 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 659 return self.func( 660 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 661 ) 662 663 return super().attimezone_sql(expression) 664 665 def trycast_sql(self, expression: exp.TryCast) -> str: 666 return self.cast_sql(expression, safe_prefix="SAFE_") 667 668 def bracket_sql(self, expression: exp.Bracket) -> str: 669 this = expression.this 670 expressions = expression.expressions 671 672 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 673 arg = expressions[0] 674 if arg.type is None: 675 from sqlglot.optimizer.annotate_types import annotate_types 676 677 arg = annotate_types(arg, dialect=self.dialect) 678 679 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 680 # BQ doesn't support bracket syntax with string values for structs 681 return f"{self.sql(this)}.{arg.name}" 682 683 expressions_sql = self.expressions(expression, flat=True) 684 offset = expression.args.get("offset") 685 686 if offset == 0: 687 expressions_sql = f"OFFSET({expressions_sql})" 688 elif offset == 1: 689 expressions_sql = f"ORDINAL({expressions_sql})" 690 elif offset is not None: 691 self.unsupported(f"Unsupported array offset: {offset}") 692 693 if expression.args.get("safe"): 694 expressions_sql = f"SAFE_{expressions_sql}" 695 696 return f"{self.sql(this)}[{expressions_sql}]" 697 698 def in_unnest_op(self, expression: exp.Unnest) -> str: 699 return self.sql(expression) 700 701 def version_sql(self, expression: exp.Version) -> str: 702 if expression.name == "TIMESTAMP": 703 expression.set("this", "SYSTEM_TIME") 704 return super().version_sql(expression) 705 706 def contains_sql(self, expression: exp.Contains) -> str: 707 this = expression.this 708 expr = expression.expression 709 710 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 711 this = this.this 712 expr = expr.this 713 714 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope")) 715 716 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 717 this = expression.this 718 719 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 720 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 721 # because they aren't literals and so the above syntax is invalid BigQuery. 722 if isinstance(this, exp.Array): 723 elem = seq_get(this.expressions, 0) 724 if not (elem and elem.find(exp.Query)): 725 return f"{self.sql(expression, 'to')}{self.sql(this)}" 726 727 return super().cast_sql(expression, safe_prefix=safe_prefix) 728 729 def clusterproperty_sql(self, expression: exp.ClusterProperty) -> str: 730 if expression.this: 731 self.unsupported(f"Unsupported CLUSTER BY {self.sql(expression, 'this')}") 732 return "" 733 return self.op_expressions("CLUSTER BY", expression)
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')
249class BigQueryGenerator(generator.Generator): 250 TRY_SUPPORTED = False 251 SUPPORTS_UESCAPE = False 252 SUPPORTS_DECODE_CASE = False 253 INTERVAL_ALLOWS_PLURAL_FORM = False 254 JOIN_HINTS = False 255 QUERY_HINTS = False 256 TABLE_HINTS = False 257 LIMIT_FETCH = "LIMIT" 258 RENAME_TABLE_WITH_DB = False 259 NVL2_SUPPORTED = False 260 UNNEST_WITH_ORDINALITY = False 261 COLLATE_IS_FUNC = True 262 LIMIT_ONLY_LITERALS = True 263 SUPPORTS_TABLE_ALIAS_COLUMNS = False 264 SUPPORTS_NAMED_CTE_COLUMNS = False 265 UNPIVOT_ALIASES_ARE_IDENTIFIERS = False 266 JSON_KEY_VALUE_PAIR_SEP = "," 267 NULL_ORDERING_SUPPORTED: bool | None = False 268 IGNORE_NULLS_IN_FUNC = True 269 JSON_PATH_SINGLE_QUOTE_ESCAPE = True 270 CAN_IMPLEMENT_ARRAY_ANY = True 271 SUPPORTS_TO_NUMBER = False 272 NAMED_PLACEHOLDER_TOKEN = "@" 273 HEX_FUNC = "TO_HEX" 274 WITH_PROPERTIES_PREFIX = "OPTIONS" 275 SUPPORTS_EXPLODING_PROJECTIONS = False 276 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 277 SUPPORTS_UNIX_SECONDS = True 278 DECLARE_DEFAULT_ASSIGNMENT = "DEFAULT" 279 280 SAFE_JSON_PATH_KEY_RE = re.compile(r"^[\-\w]*$") 281 282 WINDOW_FUNCS_WITH_NULL_ORDERING = ( 283 exp.CumeDist, 284 exp.DenseRank, 285 exp.FirstValue, 286 exp.Lag, 287 exp.LastValue, 288 exp.Lead, 289 exp.NthValue, 290 exp.Ntile, 291 exp.PercentRank, 292 exp.Rank, 293 exp.RowNumber, 294 ) 295 296 TS_OR_DS_TYPES = ( 297 exp.TsOrDsToDatetime, 298 exp.TsOrDsToTimestamp, 299 exp.TsOrDsToTime, 300 exp.TsOrDsToDate, 301 ) 302 303 TRANSFORMS = { 304 **generator.Generator.TRANSFORMS, 305 exp.AIEmbed: rename_func("EMBED"), 306 exp.AIGenerate: rename_func("GENERATE"), 307 exp.AISimilarity: rename_func("SIMILARITY"), 308 exp.ApproxTopK: rename_func("APPROX_TOP_COUNT"), 309 exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"), 310 exp.ArgMax: arg_max_or_min_no_count("MAX_BY"), 311 exp.ArgMin: arg_max_or_min_no_count("MIN_BY"), 312 exp.Array: inline_array_unless_query, 313 exp.ArrayContains: _array_contains_sql, 314 exp.ArrayFilter: filter_array_using_unnest, 315 exp.ArrayRemove: filter_array_using_unnest, 316 exp.BitwiseAndAgg: rename_func("BIT_AND"), 317 exp.BitwiseOrAgg: rename_func("BIT_OR"), 318 exp.BitwiseXorAgg: rename_func("BIT_XOR"), 319 exp.BitwiseCount: rename_func("BIT_COUNT"), 320 exp.ByteLength: rename_func("BYTE_LENGTH"), 321 exp.Cast: transforms.preprocess([transforms.remove_precision_parameterized_types]), 322 exp.CollateProperty: lambda self, e: ( 323 f"DEFAULT COLLATE {self.sql(e, 'this')}" 324 if e.args.get("default") 325 else f"COLLATE {self.sql(e, 'this')}" 326 ), 327 exp.Commit: lambda *_: "COMMIT TRANSACTION", 328 exp.CountIf: rename_func("COUNTIF"), 329 exp.Create: _create_sql, 330 exp.CTE: transforms.preprocess([_pushdown_cte_column_names]), 331 exp.DateAdd: date_add_interval_sql("DATE", "ADD"), 332 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", e.this, e.expression, unit_to_var(e)), 333 exp.DateFromParts: rename_func("DATE"), 334 exp.DateStrToDate: datestrtodate_sql, 335 exp.DateSub: date_add_interval_sql("DATE", "SUB"), 336 exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"), 337 exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"), 338 exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"), 339 exp.FromTimeZone: lambda self, e: self.func( 340 "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'" 341 ), 342 exp.GenerateSeries: generate_series_sql("GENERATE_ARRAY"), 343 exp.GroupConcat: lambda self, e: groupconcat_sql( 344 self, e, func_name="STRING_AGG", within_group=False, sep=None 345 ), 346 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 347 exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"), 348 exp.If: if_sql(false_value="NULL"), 349 exp.ILike: no_ilike_sql, 350 exp.IntDiv: rename_func("DIV"), 351 exp.Int64: rename_func("INT64"), 352 exp.JSONBool: rename_func("BOOL"), 353 exp.JSONExtract: _json_extract_sql, 354 exp.JSONExtractArray: _json_extract_sql, 355 exp.JSONExtractScalar: _json_extract_sql, 356 exp.JSONFormat: lambda self, e: self.func( 357 "TO_JSON" if e.args.get("to_json") else "TO_JSON_STRING", 358 e.this, 359 e.args.get("options"), 360 ), 361 exp.JSONKeysAtDepth: rename_func("JSON_KEYS"), 362 exp.JSONValueArray: rename_func("JSON_VALUE_ARRAY"), 363 exp.Levenshtein: _levenshtein_sql, 364 exp.Max: max_or_greatest, 365 exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)), 366 exp.MD5Digest: rename_func("MD5"), 367 exp.Min: min_or_least, 368 exp.Normalize: lambda self, e: self.func( 369 "NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE", 370 e.this, 371 e.args.get("form"), 372 ), 373 exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}", 374 exp.RegexpExtract: lambda self, e: self.func( 375 "REGEXP_EXTRACT", 376 e.this, 377 e.expression, 378 e.args.get("position"), 379 e.args.get("occurrence"), 380 ), 381 exp.RegexpExtractAll: lambda self, e: self.func("REGEXP_EXTRACT_ALL", e.this, e.expression), 382 exp.RegexpReplace: regexp_replace_sql, 383 exp.RegexpLike: rename_func("REGEXP_CONTAINS"), 384 exp.ReturnsProperty: _returnsproperty_sql, 385 exp.Rollback: lambda *_: "ROLLBACK TRANSACTION", 386 exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this), 387 exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this), 388 exp.Select: transforms.preprocess( 389 [ 390 _unnest_explode_generate_series, 391 transforms.explode_projection_to_unnest(), 392 transforms.unqualify_unnest, 393 transforms.eliminate_distinct_on, 394 _alias_ordered_group, 395 transforms.eliminate_semi_and_anti_joins, 396 ] 397 ), 398 exp.SHA: rename_func("SHA1"), 399 exp.SHA2: sha256_sql, 400 exp.SHA1Digest: rename_func("SHA1"), 401 exp.SHA2Digest: sha2_digest_sql, 402 exp.StabilityProperty: lambda self, e: ( 403 "DETERMINISTIC" if e.name == "IMMUTABLE" else "NOT DETERMINISTIC" 404 ), 405 exp.String: rename_func("STRING"), 406 exp.StrPosition: lambda self, e: strposition_sql( 407 self, e, func_name="INSTR", supports_position=True, supports_occurrence=True 408 ), 409 exp.StrToDate: _str_to_datetime_sql, 410 exp.StrToTime: _str_to_datetime_sql, 411 exp.SessionUser: lambda *_: "SESSION_USER()", 412 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 413 exp.TimeAdd: date_add_interval_sql("TIME", "ADD"), 414 exp.TimeFromParts: rename_func("TIME"), 415 exp.TimestampFromParts: rename_func("DATETIME"), 416 exp.TimeSub: date_add_interval_sql("TIME", "SUB"), 417 exp.TimestampAdd: date_add_interval_sql("TIMESTAMP", "ADD"), 418 exp.TimestampDiff: rename_func("TIMESTAMP_DIFF"), 419 exp.TimestampSub: date_add_interval_sql("TIMESTAMP", "SUB"), 420 exp.TimeStrToTime: timestrtotime_sql, 421 exp.Transaction: lambda *_: "BEGIN TRANSACTION", 422 exp.TsOrDsAdd: _ts_or_ds_add_sql, 423 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 424 exp.TsOrDsToTime: rename_func("TIME"), 425 exp.TsOrDsToDatetime: rename_func("DATETIME"), 426 exp.TsOrDsToTimestamp: rename_func("TIMESTAMP"), 427 exp.Unhex: rename_func("FROM_HEX"), 428 exp.UnixDate: rename_func("UNIX_DATE"), 429 exp.UnixToTime: _unix_to_time_sql, 430 exp.Uuid: lambda *_: "GENERATE_UUID()", 431 exp.Values: _derived_table_values_to_unnest, 432 exp.VariancePop: rename_func("VAR_POP"), 433 exp.SafeDivide: rename_func("SAFE_DIVIDE"), 434 } 435 436 SUPPORTED_JSON_PATH_PARTS = { 437 exp.JSONPathKey, 438 exp.JSONPathRoot, 439 exp.JSONPathSubscript, 440 } 441 442 TYPE_MAPPING = { 443 **generator.Generator.TYPE_MAPPING, 444 exp.DType.BIGDECIMAL: "BIGNUMERIC", 445 exp.DType.BIGINT: "INT64", 446 exp.DType.BINARY: "BYTES", 447 exp.DType.BLOB: "BYTES", 448 exp.DType.BOOLEAN: "BOOL", 449 exp.DType.CHAR: "STRING", 450 exp.DType.DECIMAL: "NUMERIC", 451 exp.DType.DOUBLE: "FLOAT64", 452 exp.DType.FLOAT: "FLOAT64", 453 exp.DType.INT: "INT64", 454 exp.DType.NCHAR: "STRING", 455 exp.DType.NVARCHAR: "STRING", 456 exp.DType.SMALLINT: "INT64", 457 exp.DType.TEXT: "STRING", 458 exp.DType.TIMESTAMP: "DATETIME", 459 exp.DType.TIMESTAMPNTZ: "DATETIME", 460 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 461 exp.DType.TIMESTAMPLTZ: "TIMESTAMP", 462 exp.DType.TINYINT: "INT64", 463 exp.DType.ROWVERSION: "BYTES", 464 exp.DType.UUID: "STRING", 465 exp.DType.VARBINARY: "BYTES", 466 exp.DType.VARCHAR: "STRING", 467 exp.DType.VARIANT: "ANY TYPE", 468 } 469 470 PROPERTIES_LOCATION = { 471 **generator.Generator.PROPERTIES_LOCATION, 472 exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, 473 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 474 } 475 476 # WINDOW comes after QUALIFY 477 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#window_clause 478 # BigQuery requires QUALIFY before WINDOW 479 AFTER_HAVING_MODIFIER_TRANSFORMS = { 480 "qualify": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["qualify"], 481 "windows": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["windows"], 482 } 483 484 # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords 485 RESERVED_KEYWORDS = { 486 "all", 487 "and", 488 "any", 489 "array", 490 "as", 491 "asc", 492 "assert_rows_modified", 493 "at", 494 "between", 495 "by", 496 "case", 497 "cast", 498 "collate", 499 "contains", 500 "create", 501 "cross", 502 "cube", 503 "current", 504 "default", 505 "define", 506 "desc", 507 "distinct", 508 "else", 509 "end", 510 "enum", 511 "escape", 512 "except", 513 "exclude", 514 "exists", 515 "extract", 516 "false", 517 "fetch", 518 "following", 519 "for", 520 "from", 521 "full", 522 "group", 523 "grouping", 524 "groups", 525 "hash", 526 "having", 527 "if", 528 "ignore", 529 "in", 530 "inner", 531 "intersect", 532 "interval", 533 "into", 534 "is", 535 "join", 536 "lateral", 537 "left", 538 "like", 539 "limit", 540 "lookup", 541 "merge", 542 "natural", 543 "new", 544 "no", 545 "not", 546 "null", 547 "nulls", 548 "of", 549 "on", 550 "or", 551 "order", 552 "outer", 553 "over", 554 "partition", 555 "preceding", 556 "proto", 557 "qualify", 558 "range", 559 "recursive", 560 "respect", 561 "right", 562 "rollup", 563 "rows", 564 "select", 565 "set", 566 "some", 567 "struct", 568 "tablesample", 569 "then", 570 "to", 571 "treat", 572 "true", 573 "unbounded", 574 "union", 575 "unnest", 576 "using", 577 "when", 578 "where", 579 "window", 580 "with", 581 "within", 582 } 583 584 def weekstart_sql(self, expression: exp.WeekStart) -> str: 585 if expression.this.name.upper() == "SUNDAY": 586 # BigQuery specific optimization since WEEK(SUNDAY) == WEEK 587 return "WEEK" 588 589 return self.func("WEEK", expression.this) 590 591 def datetrunc_sql(self, expression: exp.DateTrunc) -> str: 592 unit = expression.unit 593 unit_sql = unit.name if unit.is_string else self.sql(unit) 594 return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone")) 595 596 def mod_sql(self, expression: exp.Mod) -> str: 597 this = expression.this 598 expr = expression.expression 599 return self.func( 600 "MOD", 601 this.unnest() if isinstance(this, exp.Paren) else this, 602 expr.unnest() if isinstance(expr, exp.Paren) else expr, 603 ) 604 605 def column_parts(self, expression: exp.Column) -> str: 606 if expression.meta_get("quoted_column"): 607 # If a column reference is of the form `dataset.table`.name, we need 608 # to preserve the quoted table path, otherwise the reference breaks 609 table_parts = ".".join(p.name for p in expression.parts[:-1]) 610 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 611 return f"{table_path}.{self.sql(expression, 'this')}" 612 613 return super().column_parts(expression) 614 615 def table_parts(self, expression: exp.Table) -> str: 616 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 617 # we need to make sure the correct quoting is used in each case. 618 # 619 # For example, if there is a CTE x that clashes with a schema name, then the former will 620 # return the table y in that schema, whereas the latter will return the CTE's y column: 621 # 622 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 623 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 624 if expression.meta_get("quoted_table"): 625 table_parts = ".".join(p.name for p in expression.parts) 626 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 627 628 return super().table_parts(expression) 629 630 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 631 this = expression.this 632 if isinstance(this, exp.TsOrDsToDatetime): 633 func_name = "FORMAT_DATETIME" 634 elif isinstance(this, exp.TsOrDsToTimestamp): 635 func_name = "FORMAT_TIMESTAMP" 636 elif isinstance(this, exp.TsOrDsToTime): 637 func_name = "FORMAT_TIME" 638 else: 639 func_name = "FORMAT_DATE" 640 641 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 642 return self.func( 643 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 644 ) 645 646 def eq_sql(self, expression: exp.EQ) -> str: 647 # Operands of = cannot be NULL in BigQuery 648 if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null): 649 if not isinstance(expression.parent, exp.Update): 650 return "NULL" 651 652 return self.binary(expression, "=") 653 654 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 655 parent = expression.parent 656 657 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 658 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 659 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 660 return self.func( 661 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 662 ) 663 664 return super().attimezone_sql(expression) 665 666 def trycast_sql(self, expression: exp.TryCast) -> str: 667 return self.cast_sql(expression, safe_prefix="SAFE_") 668 669 def bracket_sql(self, expression: exp.Bracket) -> str: 670 this = expression.this 671 expressions = expression.expressions 672 673 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 674 arg = expressions[0] 675 if arg.type is None: 676 from sqlglot.optimizer.annotate_types import annotate_types 677 678 arg = annotate_types(arg, dialect=self.dialect) 679 680 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 681 # BQ doesn't support bracket syntax with string values for structs 682 return f"{self.sql(this)}.{arg.name}" 683 684 expressions_sql = self.expressions(expression, flat=True) 685 offset = expression.args.get("offset") 686 687 if offset == 0: 688 expressions_sql = f"OFFSET({expressions_sql})" 689 elif offset == 1: 690 expressions_sql = f"ORDINAL({expressions_sql})" 691 elif offset is not None: 692 self.unsupported(f"Unsupported array offset: {offset}") 693 694 if expression.args.get("safe"): 695 expressions_sql = f"SAFE_{expressions_sql}" 696 697 return f"{self.sql(this)}[{expressions_sql}]" 698 699 def in_unnest_op(self, expression: exp.Unnest) -> str: 700 return self.sql(expression) 701 702 def version_sql(self, expression: exp.Version) -> str: 703 if expression.name == "TIMESTAMP": 704 expression.set("this", "SYSTEM_TIME") 705 return super().version_sql(expression) 706 707 def contains_sql(self, expression: exp.Contains) -> str: 708 this = expression.this 709 expr = expression.expression 710 711 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 712 this = this.this 713 expr = expr.this 714 715 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope")) 716 717 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 718 this = expression.this 719 720 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 721 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 722 # because they aren't literals and so the above syntax is invalid BigQuery. 723 if isinstance(this, exp.Array): 724 elem = seq_get(this.expressions, 0) 725 if not (elem and elem.find(exp.Query)): 726 return f"{self.sql(expression, 'to')}{self.sql(this)}" 727 728 return super().cast_sql(expression, safe_prefix=safe_prefix) 729 730 def clusterproperty_sql(self, expression: exp.ClusterProperty) -> str: 731 if expression.this: 732 self.unsupported(f"Unsupported CLUSTER BY {self.sql(expression, 'this')}") 733 return "" 734 return self.op_expressions("CLUSTER BY", expression)
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.ArrayContainedBy'>: <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.properties.CalledOnNullInputProperty'>: <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.JSONBPathExists'>: <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.query.Table'>: <function preprocess.<locals>._to_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.JSONPathRoot'>, <class 'sqlglot.expressions.query.JSONPathSubscript'>}
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.CalledOnNullInputProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <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.ClusterProperty'>: <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 =
{'from', 'cast', 'natural', 'and', 'for', 'left', 'window', 'in', 'between', 'within', 'tablesample', 'rows', 'unnest', 'false', 'exclude', 'outer', 'hash', 'enum', 'exists', 'at', 'create', 'limit', 'not', 'preceding', 'cross', 'extract', 'qualify', 'where', 'if', 'set', 'join', 'following', 'default', 'any', 'of', 'unbounded', 'partition', 'union', 'assert_rows_modified', 'treat', 'current', 'all', 'cube', 'true', 'except', 'merge', 'inner', 'by', 'range', 'having', 'grouping', 'proto', 'else', 'order', 'right', 'contains', 'some', 'rollup', 'lookup', 'as', 'desc', 'recursive', 'over', 'escape', 'end', 'null', 'respect', 'on', 'to', 'is', 'when', 'struct', 'fetch', 'array', 'distinct', 'intersect', 'ignore', 'lateral', 'full', 'collate', 'into', 'nulls', 'no', 'case', 'with', 'using', 'like', 'then', 'or', 'groups', 'asc', 'interval', 'select', 'group', 'define', 'new'}
605 def column_parts(self, expression: exp.Column) -> str: 606 if expression.meta_get("quoted_column"): 607 # If a column reference is of the form `dataset.table`.name, we need 608 # to preserve the quoted table path, otherwise the reference breaks 609 table_parts = ".".join(p.name for p in expression.parts[:-1]) 610 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 611 return f"{table_path}.{self.sql(expression, 'this')}" 612 613 return super().column_parts(expression)
615 def table_parts(self, expression: exp.Table) -> str: 616 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 617 # we need to make sure the correct quoting is used in each case. 618 # 619 # For example, if there is a CTE x that clashes with a schema name, then the former will 620 # return the table y in that schema, whereas the latter will return the CTE's y column: 621 # 622 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 623 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 624 if expression.meta_get("quoted_table"): 625 table_parts = ".".join(p.name for p in expression.parts) 626 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 627 628 return super().table_parts(expression)
630 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 631 this = expression.this 632 if isinstance(this, exp.TsOrDsToDatetime): 633 func_name = "FORMAT_DATETIME" 634 elif isinstance(this, exp.TsOrDsToTimestamp): 635 func_name = "FORMAT_TIMESTAMP" 636 elif isinstance(this, exp.TsOrDsToTime): 637 func_name = "FORMAT_TIME" 638 else: 639 func_name = "FORMAT_DATE" 640 641 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 642 return self.func( 643 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 644 )
654 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 655 parent = expression.parent 656 657 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 658 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 659 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 660 return self.func( 661 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 662 ) 663 664 return super().attimezone_sql(expression)
669 def bracket_sql(self, expression: exp.Bracket) -> str: 670 this = expression.this 671 expressions = expression.expressions 672 673 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 674 arg = expressions[0] 675 if arg.type is None: 676 from sqlglot.optimizer.annotate_types import annotate_types 677 678 arg = annotate_types(arg, dialect=self.dialect) 679 680 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 681 # BQ doesn't support bracket syntax with string values for structs 682 return f"{self.sql(this)}.{arg.name}" 683 684 expressions_sql = self.expressions(expression, flat=True) 685 offset = expression.args.get("offset") 686 687 if offset == 0: 688 expressions_sql = f"OFFSET({expressions_sql})" 689 elif offset == 1: 690 expressions_sql = f"ORDINAL({expressions_sql})" 691 elif offset is not None: 692 self.unsupported(f"Unsupported array offset: {offset}") 693 694 if expression.args.get("safe"): 695 expressions_sql = f"SAFE_{expressions_sql}" 696 697 return f"{self.sql(this)}[{expressions_sql}]"
707 def contains_sql(self, expression: exp.Contains) -> str: 708 this = expression.this 709 expr = expression.expression 710 711 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 712 this = this.this 713 expr = expr.this 714 715 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:
717 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 718 this = expression.this 719 720 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 721 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 722 # because they aren't literals and so the above syntax is invalid BigQuery. 723 if isinstance(this, exp.Array): 724 elem = seq_get(this.expressions, 0) 725 if not (elem and elem.find(exp.Query)): 726 return f"{self.sql(expression, 'to')}{self.sql(this)}" 727 728 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
- AUTO_REFRESH_BARE_INTERVALS
- 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
- 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
- HISTORICAL_DATA_POST_ALIAS
- DATA_TYPE_SPECIFIERS_ALLOWED
- ENSURE_BOOLS
- CTE_RECURSIVE_KEYWORD_REQUIRED
- SUPPORTS_SINGLE_ARG_CONCAT
- LAST_DAY_SUPPORTS_DATE_PART
- PIVOT_ALIAS_WITH_AS
- 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
- JSON_PATH_KEY_QUOTED_FORCES_BRACKETS
- 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
- dynamicidentifier_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
- forclause_sql
- 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
- timeserieskey_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
- fromiso8601date_sql
- fromiso8601timestamp_sql
- fromiso8601timestampnanos_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
- strtodate_sql
- parsedatetime_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
- distancend_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
- macrooverloads_sql
- macrooverload_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_name
- chr_sql
- block_sql
- functionspecification_sql
- storedprocedure_sql
- ifblock_sql
- whileblock_sql
- execute_sql
- executesql_sql
- altermodifysqlsecurity_sql
- usingproperty_sql
- renameindex_sql