sqlglot.generators.bigquery
1from __future__ import annotations 2 3import logging 4import re 5import typing as t 6from decimal import Decimal 7from fractions import Fraction 8 9from sqlglot import exp, generator, transforms 10from sqlglot.dialects.dialect import ( 11 arg_max_or_min_no_count, 12 array_sort_order, 13 date_add_interval_sql, 14 datestrtodate_sql, 15 filter_array_using_unnest, 16 generate_series_sql, 17 if_sql, 18 inline_array_unless_query, 19 max_or_greatest, 20 min_or_least, 21 no_filter_sql, 22 no_ilike_sql, 23 regexp_replace_sql, 24 rename_func, 25 sha256_sql, 26 timestrtotime_sql, 27 ts_or_ds_add_cast, 28 unit_to_var, 29 strposition_sql, 30 groupconcat_sql, 31 sha2_digest_sql, 32) 33from sqlglot.generator import unsupported_args 34from sqlglot.helper import seq_get 35 36logger = logging.getLogger("sqlglot") 37 38JSON_EXTRACT_TYPE = t.Union[exp.JSONExtract, exp.JSONExtractScalar, exp.JSONExtractArray] 39 40DQUOTES_ESCAPING_JSON_FUNCTIONS = ("JSON_QUERY", "JSON_VALUE", "JSON_QUERY_ARRAY") 41 42 43def _derived_table_values_to_unnest(self: BigQueryGenerator, expression: exp.Values) -> str: 44 if not expression.find_ancestor(exp.From, exp.Join): 45 return self.values_sql(expression) 46 47 structs = [] 48 alias = expression.args.get("alias") 49 for tup in expression.find_all(exp.Tuple): 50 field_aliases = ( 51 alias.columns 52 if alias and alias.columns 53 else (f"_c{i}" for i in range(len(tup.expressions))) 54 ) 55 expressions = [ 56 exp.PropertyEQ(this=exp.to_identifier(name), expression=fld) 57 for name, fld in zip(field_aliases, tup.expressions) 58 ] 59 structs.append(exp.Struct(expressions=expressions)) 60 61 # Due to `UNNEST_COLUMN_ONLY`, it is expected that the table alias be contained in the columns expression 62 alias_name_only = exp.TableAlias(columns=[alias.this]) if alias else None 63 return self.unnest_sql( 64 exp.Unnest(expressions=[exp.array(*structs, copy=False)], alias=alias_name_only) 65 ) 66 67 68def _returnsproperty_sql(self: BigQueryGenerator, expression: exp.ReturnsProperty) -> str: 69 this = expression.this 70 if isinstance(this, exp.Schema): 71 this = f"{self.sql(this, 'this')} <{self.expressions(this)}>" 72 else: 73 this = self.sql(this) 74 return f"RETURNS {this}" 75 76 77def _create_sql(self: BigQueryGenerator, expression: exp.Create) -> str: 78 returns = expression.find(exp.ReturnsProperty) 79 if expression.kind == "FUNCTION" and returns and returns.args.get("is_table"): 80 expression.set("kind", "TABLE FUNCTION") 81 82 if isinstance(expression.expression, (exp.Subquery, exp.Literal)): 83 expression.set("expression", expression.expression.this) 84 85 return self.create_sql(expression) 86 87 88# https://issuetracker.google.com/issues/162294746 89# workaround for bigquery bug when grouping by an expression and then ordering 90# WITH x AS (SELECT 1 y) 91# SELECT y + 1 z 92# FROM x 93# GROUP BY x + 1 94# ORDER by z 95def _alias_ordered_group(expression: exp.Expr) -> exp.Expr: 96 if isinstance(expression, exp.Select): 97 group = expression.args.get("group") 98 order = expression.args.get("order") 99 100 if group and order: 101 aliases = { 102 select.this: select.args["alias"] 103 for select in expression.selects 104 if isinstance(select, exp.Alias) 105 } 106 107 for grouped in group.expressions: 108 if grouped.is_int: 109 continue 110 alias = aliases.get(grouped) 111 if alias: 112 grouped.replace(exp.column(alias)) 113 114 return expression 115 116 117def _pushdown_cte_column_names(expression: exp.Expr) -> exp.Expr: 118 """BigQuery doesn't allow column names when defining a CTE, so we try to push them down.""" 119 if isinstance(expression, exp.CTE) and expression.alias_column_names: 120 cte_query = expression.this 121 122 if cte_query.is_star: 123 logger.warning( 124 "Can't push down CTE column names for star queries. Run the query through" 125 " the optimizer or use 'qualify' to expand the star projections first." 126 ) 127 return expression 128 129 column_names = expression.alias_column_names 130 expression.args["alias"].set("columns", None) 131 132 for name, select in zip(column_names, cte_query.selects): 133 to_replace = select 134 135 if isinstance(select, exp.Alias): 136 select = select.this 137 138 # Inner aliases are shadowed by the CTE column names 139 to_replace.replace(exp.alias_(select, name)) 140 141 return expression 142 143 144def _unnest_explode_generate_series(expression: exp.Expr) -> exp.Expr: 145 """ 146 Rewrites exploding GENERATE_SERIES projections into table references, e.g. 147 148 SELECT GENERATE_SERIES(1, 2) AS x -> SELECT x FROM GENERATE_SERIES(1, 2) AS x 149 SELECT y, GENERATE_SERIES(1, 2) AS x FROM t -> SELECT y, x FROM t CROSS JOIN GENERATE_SERIES(1, 2) AS x 150 151 since BigQuery can't explode in the projection and must unnest it in the FROM clause instead. 152 The resulting table reference is unnested downstream by `transforms.unnest_generate_series`. 153 """ 154 if isinstance(expression, exp.Select): 155 for projection in expression.selects: 156 if isinstance(series := projection.unalias(), exp.ExplodingGenerateSeries): 157 column_name = projection.output_name or "_gen_series_value" 158 159 projection.replace(exp.column(column_name)) 160 table = exp.Table( 161 this=series, alias=exp.TableAlias(this=exp.to_identifier(column_name)) 162 ) 163 164 if expression.args.get("from_"): 165 expression.join(table, copy=False, join_type="CROSS") 166 else: 167 expression.set("from_", exp.From(this=table)) 168 169 return expression 170 171 172def _array_contains_sql(self: BigQueryGenerator, expression: exp.ArrayContains) -> str: 173 return self.sql( 174 exp.Exists( 175 this=exp.select("1") 176 .from_(exp.Unnest(expressions=[expression.left]).as_("_unnest", table=["_col"])) 177 .where(exp.column("_col").eq(expression.right)) 178 ) 179 ) 180 181 182def _widen_decimal(expression: exp.Expr) -> exp.Expr: 183 # NUMERIC holds at most 29 integer and 9 fractional digits, e.g. DECIMAL(38, 0) needs BIGNUMERIC 184 for node in expression.find_all(exp.DataType): 185 if node.is_type(exp.DType.DECIMAL) and node.expressions: 186 precision, *rest = (param.this for param in node.expressions) 187 scale = rest[0] if rest else exp.Literal.number(0) 188 if precision.is_int and scale.is_int: 189 scale_digits = int(scale.name) 190 if int(precision.name) - scale_digits > 29 or scale_digits > 9: 191 node.set("this", exp.DType.BIGDECIMAL) 192 return expression 193 194 195def _extract_part(part: str) -> t.Callable[[BigQueryGenerator, exp.Func], str]: 196 return lambda self, e: self.sql(exp.func("EXTRACT", exp.var(part), e.this, copy=False)) 197 198 199def _day_of_week_iso_sql(self: BigQueryGenerator, expression: exp.DayOfWeekIso) -> str: 200 # DAYOFWEEK is Sunday=1..Saturday=7, ISO is Monday=1..Sunday=7 201 template: exp.Expr = exp.maybe_parse("(MOD(EXTRACT(DAYOFWEEK FROM :this) + 5, 7) + 1)") 202 return self.sql(exp.replace_placeholders(template, this=expression.this)) 203 204 205def _array_agg_fold_sql( 206 self: BigQueryGenerator, expression: exp.ArrayDistinct | exp.ArraySort 207) -> str: 208 distinct = False 209 ignore_nulls = False 210 sort: exp.ArraySort | None = None 211 agg: exp.Expr = expression 212 while isinstance(agg, (exp.ArrayDistinct, exp.ArraySort)) and not agg.expression: 213 if isinstance(agg, exp.ArrayDistinct): 214 distinct = True 215 ignore_nulls |= agg.args.get("check_null") is False 216 elif sort is None: 217 sort = agg 218 agg = agg.this 219 220 order = array_sort_order(sort) if sort else None 221 222 # An ARRAY_AGG with its own ORDER BY or LIMIT can't take another 223 if ( 224 not isinstance(agg, exp.ArrayAgg) 225 or isinstance(agg.this, (exp.Order, exp.Limit)) 226 or (sort is not None and order is None) 227 ): 228 self.unsupported( 229 f"{expression.sql_name()} is only supported on an ARRAY_AGG without ORDER BY or LIMIT and with a literal order" 230 ) 231 return self.func(expression.sql_name(), expression.this, expression.expression) 232 233 value = agg.this 234 if isinstance(value, exp.Distinct): 235 distinct, value = True, value.expressions[0] 236 237 folded: exp.Expr = exp.Distinct(expressions=[value]) if distinct else value 238 if order is not None: 239 asc, nulls_first = order 240 keys = [exp.Ordered(this=value.copy(), desc=not asc or None, nulls_first=asc)] 241 242 # Aggregate ORDER BY only puts NULLs first when ascending, so other placements need a key 243 if not ignore_nulls and nulls_first is not None and nulls_first != asc: 244 if distinct: 245 self.unsupported("ARRAY_AGG(DISTINCT) only puts NULLs first when ascending") 246 else: 247 is_null = value.copy().is_(exp.null()) 248 keys.insert( 249 0, 250 exp.Ordered( 251 this=is_null, desc=nulls_first or None, nulls_first=not nulls_first 252 ), 253 ) 254 folded = exp.Order(this=folded, expressions=keys) 255 agg.set("this", folded) 256 if ignore_nulls: 257 # All-NULL input yields [], but an empty input still yields NULL. 258 agg = exp.If( 259 this=exp.Count(this=exp.Star()).eq(0), 260 true=exp.null(), 261 false=exp.func("COALESCE", exp.IgnoreNulls(this=agg), exp.array()), 262 ) 263 return self.sql(agg) 264 265 266def _ts_or_ds_add_sql(self: BigQueryGenerator, expression: exp.TsOrDsAdd) -> str: 267 return date_add_interval_sql("DATE", "ADD")(self, ts_or_ds_add_cast(expression)) 268 269 270def _ts_or_ds_diff_sql(self: BigQueryGenerator, expression: exp.TsOrDsDiff) -> str: 271 expression.this.replace(exp.cast(expression.this, exp.DType.TIMESTAMP)) 272 expression.expression.replace(exp.cast(expression.expression, exp.DType.TIMESTAMP)) 273 unit = unit_to_var(expression) 274 return self.func("DATE_DIFF", expression.this, expression.expression, unit) 275 276 277def _unix_to_time_sql(self: BigQueryGenerator, expression: exp.UnixToTime) -> str: 278 scale = expression.args.get("scale") 279 timestamp = expression.this 280 281 if scale in (None, exp.UnixToTime.SECONDS): 282 return self.func("TIMESTAMP_SECONDS", timestamp) 283 if scale == exp.UnixToTime.MILLIS: 284 return self.func("TIMESTAMP_MILLIS", timestamp) 285 if scale == exp.UnixToTime.MICROS: 286 return self.func("TIMESTAMP_MICROS", timestamp) 287 288 unix_seconds = exp.cast( 289 exp.Div(this=timestamp, expression=exp.func("POW", 10, scale)), exp.DType.BIGINT 290 ) 291 return self.func("TIMESTAMP_SECONDS", unix_seconds) 292 293 294def _str_to_datetime_sql(self: BigQueryGenerator, expression: exp.StrToDate | exp.StrToTime) -> str: 295 this = self.sql(expression, "this") 296 dtype = "DATE" if isinstance(expression, exp.StrToDate) else "TIMESTAMP" 297 298 if expression.args.get("safe"): 299 fmt = self.format_time( 300 expression, 301 self.dialect.INVERSE_FORMAT_MAPPING, 302 self.dialect.INVERSE_FORMAT_TRIE, 303 ) 304 return f"SAFE_CAST({this} AS {dtype} FORMAT {fmt})" 305 306 fmt = self.format_time(expression) 307 return self.func(f"PARSE_{dtype}", fmt, this, expression.args.get("zone")) 308 309 310@unsupported_args("ins_cost", "del_cost", "sub_cost") 311def _levenshtein_sql(self: BigQueryGenerator, expression: exp.Levenshtein) -> str: 312 max_dist = expression.args.get("max_dist") 313 if max_dist: 314 max_dist = exp.Kwarg(this=exp.var("max_distance"), expression=max_dist) 315 316 return self.func("EDIT_DISTANCE", expression.this, expression.expression, max_dist) 317 318 319def _json_extract_sql(self: BigQueryGenerator, expression: JSON_EXTRACT_TYPE) -> str: 320 upper = self.function_name(expression).upper() 321 322 dquote_escaping = upper in DQUOTES_ESCAPING_JSON_FUNCTIONS 323 324 if dquote_escaping: 325 self._quote_json_path_key_using_brackets = False 326 327 sql = rename_func(upper)(self, expression) 328 329 if dquote_escaping: 330 self._quote_json_path_key_using_brackets = True 331 332 return sql 333 334 335class BigQueryGenerator(generator.Generator): 336 TRY_SUPPORTED = False 337 SUPPORTS_UESCAPE = False 338 SUPPORTS_DECODE_CASE = False 339 INTERVAL_ALLOWS_PLURAL_FORM = False 340 JOIN_HINTS = False 341 QUERY_HINTS = False 342 TABLE_HINTS = False 343 LIMIT_FETCH = "LIMIT" 344 RENAME_TABLE_WITH_DB = False 345 NVL2_SUPPORTED = False 346 UNNEST_WITH_ORDINALITY = False 347 CAST_TO_INT_ROUNDS = True 348 COLLATE_IS_FUNC = True 349 LIMIT_ONLY_LITERALS = True 350 SUPPORTS_TABLE_ALIAS_COLUMNS = False 351 SUPPORTS_NAMED_CTE_COLUMNS = False 352 UNPIVOT_ALIASES_ARE_IDENTIFIERS = False 353 JSON_KEY_VALUE_PAIR_SEP = "," 354 NULL_ORDERING_SUPPORTED: bool | None = False 355 IGNORE_NULLS_IN_FUNC = True 356 JSON_PATH_SINGLE_QUOTE_ESCAPE = True 357 CAN_IMPLEMENT_ARRAY_ANY = True 358 SUPPORTS_TO_NUMBER = False 359 NAMED_PLACEHOLDER_TOKEN = "@" 360 HEX_FUNC = "TO_HEX" 361 WITH_PROPERTIES_PREFIX = "OPTIONS" 362 SUPPORTS_EXPLODING_PROJECTIONS = False 363 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 364 SUPPORTS_UNIX_SECONDS = True 365 DECLARE_DEFAULT_ASSIGNMENT = "DEFAULT" 366 367 SAFE_JSON_PATH_KEY_RE = re.compile(r"^[\-\w]*$") 368 369 WINDOW_FUNCS_WITH_NULL_ORDERING = ( 370 exp.CumeDist, 371 exp.DenseRank, 372 exp.FirstValue, 373 exp.Lag, 374 exp.LastValue, 375 exp.Lead, 376 exp.NthValue, 377 exp.Ntile, 378 exp.PercentRank, 379 exp.Rank, 380 exp.RowNumber, 381 ) 382 383 TS_OR_DS_TYPES: t.ClassVar = ( 384 exp.TsOrDsToDatetime, 385 exp.TsOrDsToTimestamp, 386 exp.TsOrDsToTime, 387 exp.TsOrDsToDate, 388 ) 389 390 TRANSFORMS = { 391 **generator.Generator.TRANSFORMS, 392 exp.AIEmbed: rename_func("EMBED"), 393 exp.AIGenerate: rename_func("GENERATE"), 394 exp.AISimilarity: rename_func("SIMILARITY"), 395 exp.ApproxTopK: rename_func("APPROX_TOP_COUNT"), 396 exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"), 397 exp.ArgMax: arg_max_or_min_no_count("MAX_BY"), 398 exp.ArgMin: arg_max_or_min_no_count("MIN_BY"), 399 exp.Array: inline_array_unless_query, 400 exp.ArrayContains: _array_contains_sql, 401 exp.ArrayDistinct: _array_agg_fold_sql, 402 exp.ArraySort: _array_agg_fold_sql, 403 exp.ArrayFilter: filter_array_using_unnest, 404 exp.ArrayRemove: filter_array_using_unnest, 405 exp.BitwiseAndAgg: rename_func("BIT_AND"), 406 exp.BitwiseOrAgg: rename_func("BIT_OR"), 407 exp.BitwiseXorAgg: rename_func("BIT_XOR"), 408 exp.BitwiseCount: rename_func("BIT_COUNT"), 409 exp.ByteLength: rename_func("BYTE_LENGTH"), 410 exp.Cast: transforms.preprocess( 411 [_widen_decimal, transforms.remove_precision_parameterized_types] 412 ), 413 exp.CollateProperty: lambda self, e: ( 414 f"DEFAULT COLLATE {self.sql(e, 'this')}" 415 if e.args.get("default") 416 else f"COLLATE {self.sql(e, 'this')}" 417 ), 418 exp.Commit: lambda *_: "COMMIT TRANSACTION", 419 exp.CountIf: rename_func("COUNTIF"), 420 exp.Create: _create_sql, 421 exp.CTE: transforms.preprocess([_pushdown_cte_column_names]), 422 exp.DateAdd: date_add_interval_sql("DATE", "ADD"), 423 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", e.this, e.expression, unit_to_var(e)), 424 exp.DateFromParts: rename_func("DATE"), 425 exp.DateStrToDate: datestrtodate_sql, 426 exp.DateSub: date_add_interval_sql("DATE", "SUB"), 427 exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"), 428 exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"), 429 exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"), 430 exp.Day: _extract_part("DAY"), 431 exp.DayOfMonth: _extract_part("DAY"), 432 exp.DayOfWeekIso: _day_of_week_iso_sql, 433 exp.DayOfYear: _extract_part("DAYOFYEAR"), 434 exp.Filter: no_filter_sql, 435 exp.FromTimeZone: lambda self, e: self.func( 436 "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'" 437 ), 438 exp.GenerateSeries: generate_series_sql("GENERATE_ARRAY"), 439 exp.GroupConcat: lambda self, e: groupconcat_sql( 440 self, e, func_name="STRING_AGG", within_group=False, sep=None 441 ), 442 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 443 exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"), 444 exp.Hour: _extract_part("HOUR"), 445 exp.If: if_sql(false_value="NULL"), 446 exp.ILike: no_ilike_sql, 447 exp.IntDiv: rename_func("DIV"), 448 exp.Int64: rename_func("INT64"), 449 exp.JSONBool: rename_func("BOOL"), 450 exp.JSONExtract: _json_extract_sql, 451 exp.JSONExtractArray: _json_extract_sql, 452 exp.JSONExtractScalar: _json_extract_sql, 453 exp.JSONFormat: lambda self, e: self.func( 454 "TO_JSON" if e.args.get("to_json") else "TO_JSON_STRING", 455 e.this, 456 e.args.get("options"), 457 ), 458 exp.JSONKeysAtDepth: rename_func("JSON_KEYS"), 459 exp.JSONValueArray: rename_func("JSON_VALUE_ARRAY"), 460 exp.Levenshtein: _levenshtein_sql, 461 exp.Max: max_or_greatest, 462 exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)), 463 exp.MD5Digest: rename_func("MD5"), 464 exp.Min: min_or_least, 465 exp.Month: _extract_part("MONTH"), 466 exp.Normalize: lambda self, e: self.func( 467 "NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE", 468 e.this, 469 e.args.get("form"), 470 ), 471 exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}", 472 exp.Quarter: _extract_part("QUARTER"), 473 exp.RegexpExtract: lambda self, e: self.func( 474 "REGEXP_EXTRACT", 475 e.this, 476 e.expression, 477 e.args.get("position"), 478 e.args.get("occurrence"), 479 ), 480 exp.RegexpExtractAll: lambda self, e: self.func("REGEXP_EXTRACT_ALL", e.this, e.expression), 481 exp.RegexpReplace: regexp_replace_sql, 482 exp.RegexpLike: rename_func("REGEXP_CONTAINS"), 483 exp.ReturnsProperty: _returnsproperty_sql, 484 exp.Rollback: lambda *_: "ROLLBACK TRANSACTION", 485 exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this), 486 exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this), 487 exp.Select: transforms.preprocess( 488 [ 489 _unnest_explode_generate_series, 490 transforms.explode_projection_to_unnest(), 491 transforms.unqualify_unnest, 492 transforms.eliminate_distinct_on, 493 _alias_ordered_group, 494 transforms.eliminate_semi_and_anti_joins, 495 ] 496 ), 497 exp.SHA: rename_func("SHA1"), 498 exp.SHA2: sha256_sql, 499 exp.SHA1Digest: rename_func("SHA1"), 500 exp.SHA2Digest: sha2_digest_sql, 501 exp.StabilityProperty: lambda self, e: ( 502 "DETERMINISTIC" if e.name == "IMMUTABLE" else "NOT DETERMINISTIC" 503 ), 504 exp.String: rename_func("STRING"), 505 exp.StrPosition: lambda self, e: strposition_sql( 506 self, e, func_name="INSTR", supports_position=True, supports_occurrence=True 507 ), 508 exp.StrToDate: _str_to_datetime_sql, 509 exp.StrToTime: _str_to_datetime_sql, 510 exp.SessionUser: lambda *_: "SESSION_USER()", 511 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 512 exp.TimeAdd: date_add_interval_sql("TIME", "ADD"), 513 exp.TimeFromParts: rename_func("TIME"), 514 exp.TimestampFromParts: rename_func("DATETIME"), 515 exp.TimeSub: date_add_interval_sql("TIME", "SUB"), 516 exp.TimestampAdd: date_add_interval_sql("TIMESTAMP", "ADD"), 517 exp.TimestampDiff: rename_func("TIMESTAMP_DIFF"), 518 exp.TimestampSub: date_add_interval_sql("TIMESTAMP", "SUB"), 519 exp.TimeStrToTime: timestrtotime_sql, 520 exp.Transaction: lambda *_: "BEGIN TRANSACTION", 521 exp.TsOrDsAdd: _ts_or_ds_add_sql, 522 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 523 exp.TsOrDsToTime: rename_func("TIME"), 524 exp.TsOrDsToDatetime: rename_func("DATETIME"), 525 exp.TsOrDsToTimestamp: rename_func("TIMESTAMP"), 526 exp.Unhex: rename_func("FROM_HEX"), 527 exp.UnixDate: rename_func("UNIX_DATE"), 528 exp.UnixToTime: _unix_to_time_sql, 529 exp.Uuid: lambda *_: "GENERATE_UUID()", 530 exp.Values: _derived_table_values_to_unnest, 531 exp.VariancePop: rename_func("VAR_POP"), 532 exp.WeekOfYear: _extract_part("ISOWEEK"), 533 exp.Year: _extract_part("YEAR"), 534 exp.SafeDivide: rename_func("SAFE_DIVIDE"), 535 } 536 537 SUPPORTED_JSON_PATH_PARTS = { 538 exp.JSONPathKey, 539 exp.JSONPathRoot, 540 exp.JSONPathSubscript, 541 } 542 543 TYPE_MAPPING = { 544 **generator.Generator.TYPE_MAPPING, 545 exp.DType.BIGDECIMAL: "BIGNUMERIC", 546 exp.DType.BIGINT: "INT64", 547 exp.DType.BINARY: "BYTES", 548 exp.DType.BLOB: "BYTES", 549 exp.DType.BOOLEAN: "BOOL", 550 exp.DType.CHAR: "STRING", 551 exp.DType.DECIMAL: "NUMERIC", 552 exp.DType.DOUBLE: "FLOAT64", 553 exp.DType.FLOAT: "FLOAT64", 554 exp.DType.INT: "INT64", 555 exp.DType.NCHAR: "STRING", 556 exp.DType.NVARCHAR: "STRING", 557 exp.DType.SMALLINT: "INT64", 558 exp.DType.TEXT: "STRING", 559 exp.DType.TIMESTAMP: "DATETIME", 560 exp.DType.TIMESTAMPNTZ: "DATETIME", 561 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 562 exp.DType.TIMESTAMPLTZ: "TIMESTAMP", 563 exp.DType.TINYINT: "INT64", 564 exp.DType.ROWVERSION: "BYTES", 565 exp.DType.UUID: "STRING", 566 exp.DType.VARBINARY: "BYTES", 567 exp.DType.VARCHAR: "STRING", 568 exp.DType.VARIANT: "ANY TYPE", 569 } 570 571 PROPERTIES_LOCATION = { 572 **generator.Generator.PROPERTIES_LOCATION, 573 exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, 574 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 575 } 576 577 # WINDOW comes after QUALIFY 578 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#window_clause 579 # BigQuery requires QUALIFY before WINDOW 580 AFTER_HAVING_MODIFIER_TRANSFORMS = { 581 "qualify": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["qualify"], 582 "windows": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["windows"], 583 } 584 585 # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords 586 RESERVED_KEYWORDS = { 587 "all", 588 "and", 589 "any", 590 "array", 591 "as", 592 "asc", 593 "assert_rows_modified", 594 "at", 595 "between", 596 "by", 597 "case", 598 "cast", 599 "collate", 600 "contains", 601 "create", 602 "cross", 603 "cube", 604 "current", 605 "default", 606 "define", 607 "desc", 608 "distinct", 609 "else", 610 "end", 611 "enum", 612 "escape", 613 "except", 614 "exclude", 615 "exists", 616 "extract", 617 "false", 618 "fetch", 619 "following", 620 "for", 621 "from", 622 "full", 623 "group", 624 "grouping", 625 "groups", 626 "hash", 627 "having", 628 "if", 629 "ignore", 630 "in", 631 "inner", 632 "intersect", 633 "interval", 634 "into", 635 "is", 636 "join", 637 "lateral", 638 "left", 639 "like", 640 "limit", 641 "lookup", 642 "merge", 643 "natural", 644 "new", 645 "no", 646 "not", 647 "null", 648 "nulls", 649 "of", 650 "on", 651 "or", 652 "order", 653 "outer", 654 "over", 655 "partition", 656 "preceding", 657 "proto", 658 "qualify", 659 "range", 660 "recursive", 661 "respect", 662 "right", 663 "rollup", 664 "rows", 665 "select", 666 "set", 667 "some", 668 "struct", 669 "tablesample", 670 "then", 671 "to", 672 "treat", 673 "true", 674 "unbounded", 675 "union", 676 "unnest", 677 "using", 678 "when", 679 "where", 680 "window", 681 "with", 682 "within", 683 } 684 685 def frombase_sql(self, expression: exp.FromBase) -> str: 686 # Hex is the only base BigQuery can cast from, and the sign goes before the prefix, e.g. -0xff 687 if expression.expression.name != "16": 688 self.unsupported("FROM_BASE is only supported for base 16") 689 return self.function_fallback_sql(expression) 690 691 prefixed = exp.func( 692 "REGEXP_REPLACE", 693 expression.this, 694 exp.Literal.string("^([+-]?)"), 695 exp.Literal.string("\\10x"), 696 copy=False, 697 ) 698 return self.sql(exp.cast(prefixed, exp.DType.BIGINT, copy=False)) 699 700 def approxquantile_sql(self, expression: exp.ApproxQuantile) -> str: 701 node: exp.Expr = expression 702 while node.parent and not isinstance(node.parent, (exp.Window, exp.AggFunc, exp.Select)): 703 node = node.parent 704 if isinstance(node.parent, exp.Window) and node.arg_key == "this": 705 self.unsupported("APPROX_QUANTILES does not support window functions") 706 return self.function_fallback_sql(expression) 707 708 # The k/n quantile is APPROX_QUANTILES(x, n)[k], e.g. 0.95 -> n = 20, k = 19. n is at most 709 # 100,000, so the quantile has at most 5 decimal places, and Decimal comparisons are exact 710 quantile = expression.args["quantile"] 711 weight = expression.args.get("weight") 712 value = Decimal(quantile.to_py()) if quantile.is_number else None 713 if ( 714 value is None 715 or not 0 <= value <= 1 716 or round(value, 5) != value 717 or (weight and (not weight.is_number or weight.to_py() != 1)) 718 ): 719 self.unsupported("APPROX_QUANTILE needs a literal quantile and no weight") 720 return self.function_fallback_sql(expression) 721 722 fraction = Fraction(value) 723 quantiles = exp.func("APPROX_QUANTILES", expression.this, fraction.denominator, copy=False) 724 return self.sql(quantiles[fraction.numerator]) 725 726 def weekstart_sql(self, expression: exp.WeekStart) -> str: 727 if expression.this.name.upper() == "SUNDAY": 728 # BigQuery specific optimization since WEEK(SUNDAY) == WEEK 729 return "WEEK" 730 731 return self.func("WEEK", expression.this) 732 733 def datetrunc_sql(self, expression: exp.DateTrunc) -> str: 734 unit = expression.unit 735 unit_sql = unit.name if unit.is_string else self.sql(unit) 736 return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone")) 737 738 def mod_sql(self, expression: exp.Mod) -> str: 739 this = expression.this 740 expr = expression.expression 741 return self.func( 742 "MOD", 743 this.unnest() if isinstance(this, exp.Paren) else this, 744 expr.unnest() if isinstance(expr, exp.Paren) else expr, 745 ) 746 747 def column_parts(self, expression: exp.Column) -> str: 748 if expression.meta_get("quoted_column"): 749 # If a column reference is of the form `dataset.table`.name, we need 750 # to preserve the quoted table path, otherwise the reference breaks 751 table_parts = ".".join(p.name for p in expression.parts[:-1]) 752 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 753 return f"{table_path}.{self.sql(expression, 'this')}" 754 755 return super().column_parts(expression) 756 757 def table_parts(self, expression: exp.Table) -> str: 758 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 759 # we need to make sure the correct quoting is used in each case. 760 # 761 # For example, if there is a CTE x that clashes with a schema name, then the former will 762 # return the table y in that schema, whereas the latter will return the CTE's y column: 763 # 764 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 765 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 766 if expression.meta_get("quoted_table"): 767 table_parts = ".".join(p.name for p in expression.parts) 768 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 769 770 return super().table_parts(expression) 771 772 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 773 this = expression.this 774 if isinstance(this, exp.TsOrDsToDatetime): 775 func_name = "FORMAT_DATETIME" 776 elif isinstance(this, exp.TsOrDsToTimestamp): 777 func_name = "FORMAT_TIMESTAMP" 778 elif isinstance(this, exp.TsOrDsToTime): 779 func_name = "FORMAT_TIME" 780 else: 781 func_name = "FORMAT_DATE" 782 783 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 784 return self.func( 785 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 786 ) 787 788 def eq_sql(self, expression: exp.EQ) -> str: 789 # Operands of = cannot be NULL in BigQuery 790 if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null): 791 if not isinstance(expression.parent, exp.Update): 792 return "NULL" 793 794 return self.binary(expression, "=") 795 796 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 797 parent = expression.parent 798 799 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 800 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 801 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 802 return self.func( 803 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 804 ) 805 806 return super().attimezone_sql(expression) 807 808 def trycast_sql(self, expression: exp.TryCast) -> str: 809 return self.cast_sql(expression, safe_prefix="SAFE_") 810 811 def bracket_sql(self, expression: exp.Bracket) -> str: 812 this = expression.this 813 expressions = expression.expressions 814 815 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 816 arg = expressions[0] 817 if arg.type is None: 818 from sqlglot.optimizer.annotate_types import annotate_types 819 820 arg = annotate_types(arg, dialect=self.dialect) 821 822 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 823 # BQ doesn't support bracket syntax with string values for structs 824 return f"{self.sql(this)}.{arg.name}" 825 826 expressions_sql = self.expressions(expression, flat=True) 827 offset = expression.args.get("offset") 828 829 if offset == 0: 830 expressions_sql = f"OFFSET({expressions_sql})" 831 elif offset == 1: 832 expressions_sql = f"ORDINAL({expressions_sql})" 833 elif offset is not None: 834 self.unsupported(f"Unsupported array offset: {offset}") 835 836 if expression.args.get("safe"): 837 expressions_sql = f"SAFE_{expressions_sql}" 838 839 return f"{self.sql(this)}[{expressions_sql}]" 840 841 def in_unnest_op(self, expression: exp.Unnest) -> str: 842 return self.sql(expression) 843 844 def version_sql(self, expression: exp.Version) -> str: 845 if expression.name == "TIMESTAMP": 846 expression.set("this", "SYSTEM_TIME") 847 return super().version_sql(expression) 848 849 def contains_sql(self, expression: exp.Contains) -> str: 850 this = expression.this 851 expr = expression.expression 852 853 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 854 this = this.this 855 expr = expr.this 856 857 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope")) 858 859 def parsejson_sql(self, expression: exp.ParseJSON) -> str: 860 if expression.args.get("is_literal"): 861 return f"JSON {self.sql(expression, 'this')}" 862 return super().parsejson_sql(expression) 863 864 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 865 this = expression.this 866 867 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 868 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 869 # because they aren't literals and so the above syntax is invalid BigQuery. 870 if isinstance(this, exp.Array): 871 elem = seq_get(this.expressions, 0) 872 if not (elem and elem.find(exp.Query)): 873 return f"{self.sql(expression, 'to')}{self.sql(this)}" 874 875 return super().cast_sql(expression, safe_prefix=safe_prefix) 876 877 def clusterproperty_sql(self, expression: exp.ClusterProperty) -> str: 878 if expression.this: 879 self.unsupported(f"Unsupported CLUSTER BY {self.sql(expression, 'this')}") 880 return "" 881 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')
336class BigQueryGenerator(generator.Generator): 337 TRY_SUPPORTED = False 338 SUPPORTS_UESCAPE = False 339 SUPPORTS_DECODE_CASE = False 340 INTERVAL_ALLOWS_PLURAL_FORM = False 341 JOIN_HINTS = False 342 QUERY_HINTS = False 343 TABLE_HINTS = False 344 LIMIT_FETCH = "LIMIT" 345 RENAME_TABLE_WITH_DB = False 346 NVL2_SUPPORTED = False 347 UNNEST_WITH_ORDINALITY = False 348 CAST_TO_INT_ROUNDS = True 349 COLLATE_IS_FUNC = True 350 LIMIT_ONLY_LITERALS = True 351 SUPPORTS_TABLE_ALIAS_COLUMNS = False 352 SUPPORTS_NAMED_CTE_COLUMNS = False 353 UNPIVOT_ALIASES_ARE_IDENTIFIERS = False 354 JSON_KEY_VALUE_PAIR_SEP = "," 355 NULL_ORDERING_SUPPORTED: bool | None = False 356 IGNORE_NULLS_IN_FUNC = True 357 JSON_PATH_SINGLE_QUOTE_ESCAPE = True 358 CAN_IMPLEMENT_ARRAY_ANY = True 359 SUPPORTS_TO_NUMBER = False 360 NAMED_PLACEHOLDER_TOKEN = "@" 361 HEX_FUNC = "TO_HEX" 362 WITH_PROPERTIES_PREFIX = "OPTIONS" 363 SUPPORTS_EXPLODING_PROJECTIONS = False 364 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 365 SUPPORTS_UNIX_SECONDS = True 366 DECLARE_DEFAULT_ASSIGNMENT = "DEFAULT" 367 368 SAFE_JSON_PATH_KEY_RE = re.compile(r"^[\-\w]*$") 369 370 WINDOW_FUNCS_WITH_NULL_ORDERING = ( 371 exp.CumeDist, 372 exp.DenseRank, 373 exp.FirstValue, 374 exp.Lag, 375 exp.LastValue, 376 exp.Lead, 377 exp.NthValue, 378 exp.Ntile, 379 exp.PercentRank, 380 exp.Rank, 381 exp.RowNumber, 382 ) 383 384 TS_OR_DS_TYPES: t.ClassVar = ( 385 exp.TsOrDsToDatetime, 386 exp.TsOrDsToTimestamp, 387 exp.TsOrDsToTime, 388 exp.TsOrDsToDate, 389 ) 390 391 TRANSFORMS = { 392 **generator.Generator.TRANSFORMS, 393 exp.AIEmbed: rename_func("EMBED"), 394 exp.AIGenerate: rename_func("GENERATE"), 395 exp.AISimilarity: rename_func("SIMILARITY"), 396 exp.ApproxTopK: rename_func("APPROX_TOP_COUNT"), 397 exp.ApproxDistinct: rename_func("APPROX_COUNT_DISTINCT"), 398 exp.ArgMax: arg_max_or_min_no_count("MAX_BY"), 399 exp.ArgMin: arg_max_or_min_no_count("MIN_BY"), 400 exp.Array: inline_array_unless_query, 401 exp.ArrayContains: _array_contains_sql, 402 exp.ArrayDistinct: _array_agg_fold_sql, 403 exp.ArraySort: _array_agg_fold_sql, 404 exp.ArrayFilter: filter_array_using_unnest, 405 exp.ArrayRemove: filter_array_using_unnest, 406 exp.BitwiseAndAgg: rename_func("BIT_AND"), 407 exp.BitwiseOrAgg: rename_func("BIT_OR"), 408 exp.BitwiseXorAgg: rename_func("BIT_XOR"), 409 exp.BitwiseCount: rename_func("BIT_COUNT"), 410 exp.ByteLength: rename_func("BYTE_LENGTH"), 411 exp.Cast: transforms.preprocess( 412 [_widen_decimal, transforms.remove_precision_parameterized_types] 413 ), 414 exp.CollateProperty: lambda self, e: ( 415 f"DEFAULT COLLATE {self.sql(e, 'this')}" 416 if e.args.get("default") 417 else f"COLLATE {self.sql(e, 'this')}" 418 ), 419 exp.Commit: lambda *_: "COMMIT TRANSACTION", 420 exp.CountIf: rename_func("COUNTIF"), 421 exp.Create: _create_sql, 422 exp.CTE: transforms.preprocess([_pushdown_cte_column_names]), 423 exp.DateAdd: date_add_interval_sql("DATE", "ADD"), 424 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", e.this, e.expression, unit_to_var(e)), 425 exp.DateFromParts: rename_func("DATE"), 426 exp.DateStrToDate: datestrtodate_sql, 427 exp.DateSub: date_add_interval_sql("DATE", "SUB"), 428 exp.DatetimeAdd: date_add_interval_sql("DATETIME", "ADD"), 429 exp.DatetimeSub: date_add_interval_sql("DATETIME", "SUB"), 430 exp.DateFromUnixDate: rename_func("DATE_FROM_UNIX_DATE"), 431 exp.Day: _extract_part("DAY"), 432 exp.DayOfMonth: _extract_part("DAY"), 433 exp.DayOfWeekIso: _day_of_week_iso_sql, 434 exp.DayOfYear: _extract_part("DAYOFYEAR"), 435 exp.Filter: no_filter_sql, 436 exp.FromTimeZone: lambda self, e: self.func( 437 "DATETIME", self.func("TIMESTAMP", e.this, e.args.get("zone")), "'UTC'" 438 ), 439 exp.GenerateSeries: generate_series_sql("GENERATE_ARRAY"), 440 exp.GroupConcat: lambda self, e: groupconcat_sql( 441 self, e, func_name="STRING_AGG", within_group=False, sep=None 442 ), 443 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 444 exp.HexString: lambda self, e: self.hexstring_sql(e, binary_function_repr="FROM_HEX"), 445 exp.Hour: _extract_part("HOUR"), 446 exp.If: if_sql(false_value="NULL"), 447 exp.ILike: no_ilike_sql, 448 exp.IntDiv: rename_func("DIV"), 449 exp.Int64: rename_func("INT64"), 450 exp.JSONBool: rename_func("BOOL"), 451 exp.JSONExtract: _json_extract_sql, 452 exp.JSONExtractArray: _json_extract_sql, 453 exp.JSONExtractScalar: _json_extract_sql, 454 exp.JSONFormat: lambda self, e: self.func( 455 "TO_JSON" if e.args.get("to_json") else "TO_JSON_STRING", 456 e.this, 457 e.args.get("options"), 458 ), 459 exp.JSONKeysAtDepth: rename_func("JSON_KEYS"), 460 exp.JSONValueArray: rename_func("JSON_VALUE_ARRAY"), 461 exp.Levenshtein: _levenshtein_sql, 462 exp.Max: max_or_greatest, 463 exp.MD5: lambda self, e: self.func("TO_HEX", self.func("MD5", e.this)), 464 exp.MD5Digest: rename_func("MD5"), 465 exp.Min: min_or_least, 466 exp.Month: _extract_part("MONTH"), 467 exp.Normalize: lambda self, e: self.func( 468 "NORMALIZE_AND_CASEFOLD" if e.args.get("is_casefold") else "NORMALIZE", 469 e.this, 470 e.args.get("form"), 471 ), 472 exp.PartitionedByProperty: lambda self, e: f"PARTITION BY {self.sql(e, 'this')}", 473 exp.Quarter: _extract_part("QUARTER"), 474 exp.RegexpExtract: lambda self, e: self.func( 475 "REGEXP_EXTRACT", 476 e.this, 477 e.expression, 478 e.args.get("position"), 479 e.args.get("occurrence"), 480 ), 481 exp.RegexpExtractAll: lambda self, e: self.func("REGEXP_EXTRACT_ALL", e.this, e.expression), 482 exp.RegexpReplace: regexp_replace_sql, 483 exp.RegexpLike: rename_func("REGEXP_CONTAINS"), 484 exp.ReturnsProperty: _returnsproperty_sql, 485 exp.Rollback: lambda *_: "ROLLBACK TRANSACTION", 486 exp.ParseTime: lambda self, e: self.func("PARSE_TIME", self.format_time(e), e.this), 487 exp.ParseDatetime: lambda self, e: self.func("PARSE_DATETIME", self.format_time(e), e.this), 488 exp.Select: transforms.preprocess( 489 [ 490 _unnest_explode_generate_series, 491 transforms.explode_projection_to_unnest(), 492 transforms.unqualify_unnest, 493 transforms.eliminate_distinct_on, 494 _alias_ordered_group, 495 transforms.eliminate_semi_and_anti_joins, 496 ] 497 ), 498 exp.SHA: rename_func("SHA1"), 499 exp.SHA2: sha256_sql, 500 exp.SHA1Digest: rename_func("SHA1"), 501 exp.SHA2Digest: sha2_digest_sql, 502 exp.StabilityProperty: lambda self, e: ( 503 "DETERMINISTIC" if e.name == "IMMUTABLE" else "NOT DETERMINISTIC" 504 ), 505 exp.String: rename_func("STRING"), 506 exp.StrPosition: lambda self, e: strposition_sql( 507 self, e, func_name="INSTR", supports_position=True, supports_occurrence=True 508 ), 509 exp.StrToDate: _str_to_datetime_sql, 510 exp.StrToTime: _str_to_datetime_sql, 511 exp.SessionUser: lambda *_: "SESSION_USER()", 512 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 513 exp.TimeAdd: date_add_interval_sql("TIME", "ADD"), 514 exp.TimeFromParts: rename_func("TIME"), 515 exp.TimestampFromParts: rename_func("DATETIME"), 516 exp.TimeSub: date_add_interval_sql("TIME", "SUB"), 517 exp.TimestampAdd: date_add_interval_sql("TIMESTAMP", "ADD"), 518 exp.TimestampDiff: rename_func("TIMESTAMP_DIFF"), 519 exp.TimestampSub: date_add_interval_sql("TIMESTAMP", "SUB"), 520 exp.TimeStrToTime: timestrtotime_sql, 521 exp.Transaction: lambda *_: "BEGIN TRANSACTION", 522 exp.TsOrDsAdd: _ts_or_ds_add_sql, 523 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 524 exp.TsOrDsToTime: rename_func("TIME"), 525 exp.TsOrDsToDatetime: rename_func("DATETIME"), 526 exp.TsOrDsToTimestamp: rename_func("TIMESTAMP"), 527 exp.Unhex: rename_func("FROM_HEX"), 528 exp.UnixDate: rename_func("UNIX_DATE"), 529 exp.UnixToTime: _unix_to_time_sql, 530 exp.Uuid: lambda *_: "GENERATE_UUID()", 531 exp.Values: _derived_table_values_to_unnest, 532 exp.VariancePop: rename_func("VAR_POP"), 533 exp.WeekOfYear: _extract_part("ISOWEEK"), 534 exp.Year: _extract_part("YEAR"), 535 exp.SafeDivide: rename_func("SAFE_DIVIDE"), 536 } 537 538 SUPPORTED_JSON_PATH_PARTS = { 539 exp.JSONPathKey, 540 exp.JSONPathRoot, 541 exp.JSONPathSubscript, 542 } 543 544 TYPE_MAPPING = { 545 **generator.Generator.TYPE_MAPPING, 546 exp.DType.BIGDECIMAL: "BIGNUMERIC", 547 exp.DType.BIGINT: "INT64", 548 exp.DType.BINARY: "BYTES", 549 exp.DType.BLOB: "BYTES", 550 exp.DType.BOOLEAN: "BOOL", 551 exp.DType.CHAR: "STRING", 552 exp.DType.DECIMAL: "NUMERIC", 553 exp.DType.DOUBLE: "FLOAT64", 554 exp.DType.FLOAT: "FLOAT64", 555 exp.DType.INT: "INT64", 556 exp.DType.NCHAR: "STRING", 557 exp.DType.NVARCHAR: "STRING", 558 exp.DType.SMALLINT: "INT64", 559 exp.DType.TEXT: "STRING", 560 exp.DType.TIMESTAMP: "DATETIME", 561 exp.DType.TIMESTAMPNTZ: "DATETIME", 562 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 563 exp.DType.TIMESTAMPLTZ: "TIMESTAMP", 564 exp.DType.TINYINT: "INT64", 565 exp.DType.ROWVERSION: "BYTES", 566 exp.DType.UUID: "STRING", 567 exp.DType.VARBINARY: "BYTES", 568 exp.DType.VARCHAR: "STRING", 569 exp.DType.VARIANT: "ANY TYPE", 570 } 571 572 PROPERTIES_LOCATION = { 573 **generator.Generator.PROPERTIES_LOCATION, 574 exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, 575 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 576 } 577 578 # WINDOW comes after QUALIFY 579 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#window_clause 580 # BigQuery requires QUALIFY before WINDOW 581 AFTER_HAVING_MODIFIER_TRANSFORMS = { 582 "qualify": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["qualify"], 583 "windows": generator.AFTER_HAVING_MODIFIER_TRANSFORMS["windows"], 584 } 585 586 # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords 587 RESERVED_KEYWORDS = { 588 "all", 589 "and", 590 "any", 591 "array", 592 "as", 593 "asc", 594 "assert_rows_modified", 595 "at", 596 "between", 597 "by", 598 "case", 599 "cast", 600 "collate", 601 "contains", 602 "create", 603 "cross", 604 "cube", 605 "current", 606 "default", 607 "define", 608 "desc", 609 "distinct", 610 "else", 611 "end", 612 "enum", 613 "escape", 614 "except", 615 "exclude", 616 "exists", 617 "extract", 618 "false", 619 "fetch", 620 "following", 621 "for", 622 "from", 623 "full", 624 "group", 625 "grouping", 626 "groups", 627 "hash", 628 "having", 629 "if", 630 "ignore", 631 "in", 632 "inner", 633 "intersect", 634 "interval", 635 "into", 636 "is", 637 "join", 638 "lateral", 639 "left", 640 "like", 641 "limit", 642 "lookup", 643 "merge", 644 "natural", 645 "new", 646 "no", 647 "not", 648 "null", 649 "nulls", 650 "of", 651 "on", 652 "or", 653 "order", 654 "outer", 655 "over", 656 "partition", 657 "preceding", 658 "proto", 659 "qualify", 660 "range", 661 "recursive", 662 "respect", 663 "right", 664 "rollup", 665 "rows", 666 "select", 667 "set", 668 "some", 669 "struct", 670 "tablesample", 671 "then", 672 "to", 673 "treat", 674 "true", 675 "unbounded", 676 "union", 677 "unnest", 678 "using", 679 "when", 680 "where", 681 "window", 682 "with", 683 "within", 684 } 685 686 def frombase_sql(self, expression: exp.FromBase) -> str: 687 # Hex is the only base BigQuery can cast from, and the sign goes before the prefix, e.g. -0xff 688 if expression.expression.name != "16": 689 self.unsupported("FROM_BASE is only supported for base 16") 690 return self.function_fallback_sql(expression) 691 692 prefixed = exp.func( 693 "REGEXP_REPLACE", 694 expression.this, 695 exp.Literal.string("^([+-]?)"), 696 exp.Literal.string("\\10x"), 697 copy=False, 698 ) 699 return self.sql(exp.cast(prefixed, exp.DType.BIGINT, copy=False)) 700 701 def approxquantile_sql(self, expression: exp.ApproxQuantile) -> str: 702 node: exp.Expr = expression 703 while node.parent and not isinstance(node.parent, (exp.Window, exp.AggFunc, exp.Select)): 704 node = node.parent 705 if isinstance(node.parent, exp.Window) and node.arg_key == "this": 706 self.unsupported("APPROX_QUANTILES does not support window functions") 707 return self.function_fallback_sql(expression) 708 709 # The k/n quantile is APPROX_QUANTILES(x, n)[k], e.g. 0.95 -> n = 20, k = 19. n is at most 710 # 100,000, so the quantile has at most 5 decimal places, and Decimal comparisons are exact 711 quantile = expression.args["quantile"] 712 weight = expression.args.get("weight") 713 value = Decimal(quantile.to_py()) if quantile.is_number else None 714 if ( 715 value is None 716 or not 0 <= value <= 1 717 or round(value, 5) != value 718 or (weight and (not weight.is_number or weight.to_py() != 1)) 719 ): 720 self.unsupported("APPROX_QUANTILE needs a literal quantile and no weight") 721 return self.function_fallback_sql(expression) 722 723 fraction = Fraction(value) 724 quantiles = exp.func("APPROX_QUANTILES", expression.this, fraction.denominator, copy=False) 725 return self.sql(quantiles[fraction.numerator]) 726 727 def weekstart_sql(self, expression: exp.WeekStart) -> str: 728 if expression.this.name.upper() == "SUNDAY": 729 # BigQuery specific optimization since WEEK(SUNDAY) == WEEK 730 return "WEEK" 731 732 return self.func("WEEK", expression.this) 733 734 def datetrunc_sql(self, expression: exp.DateTrunc) -> str: 735 unit = expression.unit 736 unit_sql = unit.name if unit.is_string else self.sql(unit) 737 return self.func("DATE_TRUNC", expression.this, unit_sql, expression.args.get("zone")) 738 739 def mod_sql(self, expression: exp.Mod) -> str: 740 this = expression.this 741 expr = expression.expression 742 return self.func( 743 "MOD", 744 this.unnest() if isinstance(this, exp.Paren) else this, 745 expr.unnest() if isinstance(expr, exp.Paren) else expr, 746 ) 747 748 def column_parts(self, expression: exp.Column) -> str: 749 if expression.meta_get("quoted_column"): 750 # If a column reference is of the form `dataset.table`.name, we need 751 # to preserve the quoted table path, otherwise the reference breaks 752 table_parts = ".".join(p.name for p in expression.parts[:-1]) 753 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 754 return f"{table_path}.{self.sql(expression, 'this')}" 755 756 return super().column_parts(expression) 757 758 def table_parts(self, expression: exp.Table) -> str: 759 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 760 # we need to make sure the correct quoting is used in each case. 761 # 762 # For example, if there is a CTE x that clashes with a schema name, then the former will 763 # return the table y in that schema, whereas the latter will return the CTE's y column: 764 # 765 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 766 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 767 if expression.meta_get("quoted_table"): 768 table_parts = ".".join(p.name for p in expression.parts) 769 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 770 771 return super().table_parts(expression) 772 773 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 774 this = expression.this 775 if isinstance(this, exp.TsOrDsToDatetime): 776 func_name = "FORMAT_DATETIME" 777 elif isinstance(this, exp.TsOrDsToTimestamp): 778 func_name = "FORMAT_TIMESTAMP" 779 elif isinstance(this, exp.TsOrDsToTime): 780 func_name = "FORMAT_TIME" 781 else: 782 func_name = "FORMAT_DATE" 783 784 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 785 return self.func( 786 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 787 ) 788 789 def eq_sql(self, expression: exp.EQ) -> str: 790 # Operands of = cannot be NULL in BigQuery 791 if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null): 792 if not isinstance(expression.parent, exp.Update): 793 return "NULL" 794 795 return self.binary(expression, "=") 796 797 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 798 parent = expression.parent 799 800 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 801 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 802 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 803 return self.func( 804 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 805 ) 806 807 return super().attimezone_sql(expression) 808 809 def trycast_sql(self, expression: exp.TryCast) -> str: 810 return self.cast_sql(expression, safe_prefix="SAFE_") 811 812 def bracket_sql(self, expression: exp.Bracket) -> str: 813 this = expression.this 814 expressions = expression.expressions 815 816 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 817 arg = expressions[0] 818 if arg.type is None: 819 from sqlglot.optimizer.annotate_types import annotate_types 820 821 arg = annotate_types(arg, dialect=self.dialect) 822 823 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 824 # BQ doesn't support bracket syntax with string values for structs 825 return f"{self.sql(this)}.{arg.name}" 826 827 expressions_sql = self.expressions(expression, flat=True) 828 offset = expression.args.get("offset") 829 830 if offset == 0: 831 expressions_sql = f"OFFSET({expressions_sql})" 832 elif offset == 1: 833 expressions_sql = f"ORDINAL({expressions_sql})" 834 elif offset is not None: 835 self.unsupported(f"Unsupported array offset: {offset}") 836 837 if expression.args.get("safe"): 838 expressions_sql = f"SAFE_{expressions_sql}" 839 840 return f"{self.sql(this)}[{expressions_sql}]" 841 842 def in_unnest_op(self, expression: exp.Unnest) -> str: 843 return self.sql(expression) 844 845 def version_sql(self, expression: exp.Version) -> str: 846 if expression.name == "TIMESTAMP": 847 expression.set("this", "SYSTEM_TIME") 848 return super().version_sql(expression) 849 850 def contains_sql(self, expression: exp.Contains) -> str: 851 this = expression.this 852 expr = expression.expression 853 854 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 855 this = this.this 856 expr = expr.this 857 858 return self.func("CONTAINS_SUBSTR", this, expr, expression.args.get("json_scope")) 859 860 def parsejson_sql(self, expression: exp.ParseJSON) -> str: 861 if expression.args.get("is_literal"): 862 return f"JSON {self.sql(expression, 'this')}" 863 return super().parsejson_sql(expression) 864 865 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 866 this = expression.this 867 868 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 869 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 870 # because they aren't literals and so the above syntax is invalid BigQuery. 871 if isinstance(this, exp.Array): 872 elem = seq_get(this.expressions, 0) 873 if not (elem and elem.find(exp.Query)): 874 return f"{self.sql(expression, 'to')}{self.sql(this)}" 875 876 return super().cast_sql(expression, safe_prefix=safe_prefix) 877 878 def clusterproperty_sql(self, expression: exp.ClusterProperty) -> str: 879 if expression.this: 880 self.unsupported(f"Unsupported CLUSTER BY {self.sql(expression, 'this')}") 881 return "" 882 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: ClassVar =
(<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.BinaryColumnConstraint'>: <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.JSONBContainsTopKey'>: <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.ArrayDistinct'>: <function _array_agg_fold_sql>, <class 'sqlglot.expressions.array.ArraySort'>: <function _array_agg_fold_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.temporal.Day'>: <function _extract_part.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.DayOfMonth'>: <function _extract_part.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.DayOfWeekIso'>: <function _day_of_week_iso_sql>, <class 'sqlglot.expressions.temporal.DayOfYear'>: <function _extract_part.<locals>.<lambda>>, <class 'sqlglot.expressions.core.Filter'>: <function no_filter_sql>, <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.temporal.Hour'>: <function _extract_part.<locals>.<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.temporal.Month'>: <function _extract_part.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Normalize'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionedByProperty'>: <function BigQueryGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.Quarter'>: <function _extract_part.<locals>.<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.temporal.WeekOfYear'>: <function _extract_part.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.Year'>: <function _extract_part.<locals>.<lambda>>, <class 'sqlglot.expressions.math.SafeDivide'>: <function rename_func.<locals>.<lambda>>}
SUPPORTED_JSON_PATH_PARTS =
{<class 'sqlglot.expressions.query.JSONPathRoot'>, <class 'sqlglot.expressions.query.JSONPathSubscript'>, <class 'sqlglot.expressions.query.JSONPathKey'>}
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.AggregateKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <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.properties.PhysicalProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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.StorageProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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.TableSpaceProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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 =
{'into', 'some', 'merge', 'at', 'outer', 'true', 'ignore', 'cast', 'following', 'where', 'of', 'proto', 'rows', 'distinct', 'group', 'define', 'null', 'grouping', 'fetch', 'hash', 'lookup', 'using', 'false', 'enum', 'end', 'and', 'inner', 'on', 'when', 'struct', 'full', 'left', 'new', 'else', 'treat', 'escape', 'union', 'collate', 'case', 'rollup', 'as', 'except', 'desc', 'by', 'extract', 'preceding', 'between', 'to', 'unnest', 'cross', 'assert_rows_modified', 'natural', 'groups', 'set', 'current', 'for', 'all', 'intersect', 'array', 'within', 'limit', 'from', 'exclude', 'recursive', 'select', 'over', 'default', 'or', 'cube', 'having', 'window', 'create', 'not', 'interval', 'is', 'order', 'right', 'tablesample', 'like', 'lateral', 'unbounded', 'qualify', 'any', 'partition', 'respect', 'nulls', 'asc', 'no', 'exists', 'range', 'contains', 'then', 'if', 'join', 'in', 'with'}
686 def frombase_sql(self, expression: exp.FromBase) -> str: 687 # Hex is the only base BigQuery can cast from, and the sign goes before the prefix, e.g. -0xff 688 if expression.expression.name != "16": 689 self.unsupported("FROM_BASE is only supported for base 16") 690 return self.function_fallback_sql(expression) 691 692 prefixed = exp.func( 693 "REGEXP_REPLACE", 694 expression.this, 695 exp.Literal.string("^([+-]?)"), 696 exp.Literal.string("\\10x"), 697 copy=False, 698 ) 699 return self.sql(exp.cast(prefixed, exp.DType.BIGINT, copy=False))
701 def approxquantile_sql(self, expression: exp.ApproxQuantile) -> str: 702 node: exp.Expr = expression 703 while node.parent and not isinstance(node.parent, (exp.Window, exp.AggFunc, exp.Select)): 704 node = node.parent 705 if isinstance(node.parent, exp.Window) and node.arg_key == "this": 706 self.unsupported("APPROX_QUANTILES does not support window functions") 707 return self.function_fallback_sql(expression) 708 709 # The k/n quantile is APPROX_QUANTILES(x, n)[k], e.g. 0.95 -> n = 20, k = 19. n is at most 710 # 100,000, so the quantile has at most 5 decimal places, and Decimal comparisons are exact 711 quantile = expression.args["quantile"] 712 weight = expression.args.get("weight") 713 value = Decimal(quantile.to_py()) if quantile.is_number else None 714 if ( 715 value is None 716 or not 0 <= value <= 1 717 or round(value, 5) != value 718 or (weight and (not weight.is_number or weight.to_py() != 1)) 719 ): 720 self.unsupported("APPROX_QUANTILE needs a literal quantile and no weight") 721 return self.function_fallback_sql(expression) 722 723 fraction = Fraction(value) 724 quantiles = exp.func("APPROX_QUANTILES", expression.this, fraction.denominator, copy=False) 725 return self.sql(quantiles[fraction.numerator])
748 def column_parts(self, expression: exp.Column) -> str: 749 if expression.meta_get("quoted_column"): 750 # If a column reference is of the form `dataset.table`.name, we need 751 # to preserve the quoted table path, otherwise the reference breaks 752 table_parts = ".".join(p.name for p in expression.parts[:-1]) 753 table_path = self.sql(exp.Identifier(this=table_parts, quoted=True)) 754 return f"{table_path}.{self.sql(expression, 'this')}" 755 756 return super().column_parts(expression)
758 def table_parts(self, expression: exp.Table) -> str: 759 # Depending on the context, `x.y` may not resolve to the same data source as `x`.`y`, so 760 # we need to make sure the correct quoting is used in each case. 761 # 762 # For example, if there is a CTE x that clashes with a schema name, then the former will 763 # return the table y in that schema, whereas the latter will return the CTE's y column: 764 # 765 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x.y` -> cross join 766 # - WITH x AS (SELECT [1, 2] AS y) SELECT * FROM x, `x`.`y` -> implicit unnest 767 if expression.meta_get("quoted_table"): 768 table_parts = ".".join(p.name for p in expression.parts) 769 return self.sql(exp.Identifier(this=table_parts, quoted=True)) 770 771 return super().table_parts(expression)
773 def timetostr_sql(self, expression: exp.TimeToStr) -> str: 774 this = expression.this 775 if isinstance(this, exp.TsOrDsToDatetime): 776 func_name = "FORMAT_DATETIME" 777 elif isinstance(this, exp.TsOrDsToTimestamp): 778 func_name = "FORMAT_TIMESTAMP" 779 elif isinstance(this, exp.TsOrDsToTime): 780 func_name = "FORMAT_TIME" 781 else: 782 func_name = "FORMAT_DATE" 783 784 time_expr = this if isinstance(this, self.TS_OR_DS_TYPES) else expression 785 return self.func( 786 func_name, self.format_time(expression), time_expr.this, expression.args.get("zone") 787 )
797 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 798 parent = expression.parent 799 800 # BigQuery allows CAST(.. AS {STRING|TIMESTAMP} [FORMAT <fmt> [AT TIME ZONE <tz>]]). 801 # Only the TIMESTAMP one should use the below conversion, when AT TIME ZONE is included. 802 if not isinstance(parent, exp.Cast) or not parent.to.is_type("text"): 803 return self.func( 804 "TIMESTAMP", self.func("DATETIME", expression.this, expression.args.get("zone")) 805 ) 806 807 return super().attimezone_sql(expression)
812 def bracket_sql(self, expression: exp.Bracket) -> str: 813 this = expression.this 814 expressions = expression.expressions 815 816 if len(expressions) == 1 and this and this.is_type(exp.DType.STRUCT): 817 arg = expressions[0] 818 if arg.type is None: 819 from sqlglot.optimizer.annotate_types import annotate_types 820 821 arg = annotate_types(arg, dialect=self.dialect) 822 823 if arg.type and arg.type.this in exp.DataType.TEXT_TYPES: 824 # BQ doesn't support bracket syntax with string values for structs 825 return f"{self.sql(this)}.{arg.name}" 826 827 expressions_sql = self.expressions(expression, flat=True) 828 offset = expression.args.get("offset") 829 830 if offset == 0: 831 expressions_sql = f"OFFSET({expressions_sql})" 832 elif offset == 1: 833 expressions_sql = f"ORDINAL({expressions_sql})" 834 elif offset is not None: 835 self.unsupported(f"Unsupported array offset: {offset}") 836 837 if expression.args.get("safe"): 838 expressions_sql = f"SAFE_{expressions_sql}" 839 840 return f"{self.sql(this)}[{expressions_sql}]"
850 def contains_sql(self, expression: exp.Contains) -> str: 851 this = expression.this 852 expr = expression.expression 853 854 if isinstance(this, exp.Lower) and isinstance(expr, exp.Lower): 855 this = this.this 856 expr = expr.this 857 858 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:
865 def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str: 866 this = expression.this 867 868 # This ensures that inline type-annotated ARRAY literals like ARRAY<INT64>[1, 2, 3] 869 # are roundtripped unaffected. The inner check excludes ARRAY(SELECT ...) expressions, 870 # because they aren't literals and so the above syntax is invalid BigQuery. 871 if isinstance(this, exp.Array): 872 elem = seq_get(this.expressions, 0) 873 if not (elem and elem.find(exp.Query)): 874 return f"{self.sql(expression, 'to')}{self.sql(this)}" 875 876 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
- SUPPORTS_GROUPING_SETS_AS_SUFFIX
- INDEX_ON
- INOUT_SEPARATOR
- DIRECTED_JOINS
- QUERY_HINT_SEP
- IS_BOOL_ALLOWED
- SAFE_INT_DIVISION
- 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
- SUPPORTS_ALTER_COLUMN_NULLABILITY
- SUPPORTS_ALTER_COLUMN_IF_EXISTS
- 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
- SET_OP_LIMITS
- SET_OP_PARENTHESIZED_OPERANDS
- COPY_PARAMS_ARE_WRAPPED
- COPY_PARAMS_EQ_REQUIRED
- COPY_HAS_INTO_KEYWORD
- UNICODE_SUBSTITUTE
- STAR_EXCEPT
- BYTE_STRING_FUNCTION
- 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
- MOD_OPERATOR
- MOD_PAREN_PARENT_TYPES
- ALTER_PARTITION_OPTIONS
- 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
- lockingstatement_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
- partitionbyexcluding_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
- replacecolumns_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
- function_name
- 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
- aggregatekeyproperty_sql
- duplicatekeyproperty_sql
- uniquekeyproperty_sql
- distributedbyproperty_sql
- oncluster_sql
- clusteredbyproperty_sql
- anyvalue_sql
- querytransform_sql
- indexconstraintoption_sql
- checkcolumnconstraint_sql
- indexcolumnconstraint_sql
- nvl2_sql
- nthvalue_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
- arrayinsert_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
- 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
- arraysort_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
- liststage_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
- weightstring_sql
- block_sql
- functionspecification_sql
- storedprocedure_sql
- ifblock_sql
- casestatement_sql
- whileblock_sql
- loopblock_sql
- repeatblock_sql
- leave_sql
- iterate_sql
- execute_sql
- executesql_sql
- altermodifysqlsecurity_sql
- usingproperty_sql
- renameindex_sql