sqlglot.generators.presto
1from __future__ import annotations 2 3import typing as t 4 5from sqlglot import exp, generator, transforms 6from sqlglot.dialects.dialect import ( 7 bool_xor_sql, 8 bracket_to_element_at_sql, 9 datestrtodate_sql, 10 encode_decode_sql, 11 if_sql, 12 left_to_substring_sql, 13 no_ilike_sql, 14 no_pivot_sql, 15 no_timestamp_sql, 16 regexp_extract_sql, 17 rename_func, 18 right_to_substring_sql, 19 sha256_sql, 20 strposition_sql, 21 struct_extract_sql, 22 timestamptrunc_sql, 23 timestrtotime_sql, 24 ts_or_ds_add_cast, 25 unit_to_str, 26 sequence_sql, 27 explode_to_unnest_sql, 28 sha2_digest_sql, 29) 30from sqlglot.dialects.hive import Hive 31from sqlglot.generator import unsupported_args 32from sqlglot.optimizer.scope import find_all_in_scope 33from sqlglot.transforms import unqualify_columns 34 35DATE_ADD_OR_SUB = t.Union[exp.DateAdd, exp.TimestampAdd, exp.DateSub] 36 37 38def _initcap_sql(self: PrestoGenerator, expression: exp.Initcap) -> str: 39 delimiters = expression.expression 40 if delimiters and not ( 41 delimiters.is_string and delimiters.this == self.dialect.INITCAP_DEFAULT_DELIMITER_CHARS 42 ): 43 self.unsupported("INITCAP does not support custom delimiters") 44 45 regex = r"(\w)(\w*)" 46 return f"REGEXP_REPLACE({self.sql(expression, 'this')}, '{regex}', x -> UPPER(x[1]) || LOWER(x[2]))" 47 48 49def _no_sort_array(self: PrestoGenerator, expression: exp.SortArray) -> str: 50 if expression.args.get("asc") == exp.false(): 51 comparator = "(a, b) -> CASE WHEN a < b THEN 1 WHEN a > b THEN -1 ELSE 0 END" 52 else: 53 comparator = None 54 return self.func("ARRAY_SORT", expression.this, comparator) 55 56 57def _schema_sql(self: PrestoGenerator, expression: exp.Schema) -> str: 58 if isinstance(expression.parent, exp.PartitionedByProperty): 59 # Any columns in the ARRAY[] string literals should not be quoted 60 expression.transform(lambda n: n.name if isinstance(n, exp.Identifier) else n, copy=False) 61 62 partition_exprs = [ 63 self.sql(c) if isinstance(c, (exp.Func, exp.Property)) else self.sql(c, "this") 64 for c in expression.expressions 65 ] 66 return self.sql(exp.Array(expressions=[exp.Literal.string(c) for c in partition_exprs])) 67 68 if expression.parent: 69 for schema in expression.parent.find_all(exp.Schema): 70 if schema is expression: 71 continue 72 73 column_defs = schema.find_all(exp.ColumnDef) 74 if column_defs and isinstance(schema.parent, exp.Property): 75 expression.expressions.extend(column_defs) 76 77 return self.schema_sql(expression) 78 79 80def _quantile_sql(self: PrestoGenerator, expression: exp.Quantile) -> str: 81 self.unsupported("Presto does not support exact quantiles") 82 return self.func("APPROX_PERCENTILE", expression.this, expression.args.get("quantile")) 83 84 85def _str_to_time_sql( 86 self: PrestoGenerator, expression: exp.StrToDate | exp.StrToTime | exp.TsOrDsToDate 87) -> str: 88 return self.func("DATE_PARSE", expression.this, self.format_time(expression)) 89 90 91def _ts_or_ds_to_date_sql(self: PrestoGenerator, expression: exp.TsOrDsToDate) -> str: 92 time_format = self.format_time(expression) 93 dialect_class = type(self.dialect) 94 if time_format and time_format not in (dialect_class.TIME_FORMAT, dialect_class.DATE_FORMAT): 95 return self.sql(exp.cast(_str_to_time_sql(self, expression), exp.DType.DATE)) 96 return self.sql(exp.cast(exp.cast(expression.this, exp.DType.TIMESTAMP), exp.DType.DATE)) 97 98 99def _ts_or_ds_add_sql(self: PrestoGenerator, expression: exp.TsOrDsAdd) -> str: 100 expression = ts_or_ds_add_cast(expression) 101 unit = unit_to_str(expression) 102 return self.func("DATE_ADD", unit, expression.expression, expression.this) 103 104 105def _ts_or_ds_diff_sql(self: PrestoGenerator, expression: exp.TsOrDsDiff) -> str: 106 this = exp.cast(expression.this, exp.DType.TIMESTAMP) 107 expr = exp.cast(expression.expression, exp.DType.TIMESTAMP) 108 unit = unit_to_str(expression) 109 return self.func("DATE_DIFF", unit, expr, this) 110 111 112def _first_last_sql(self: PrestoGenerator, expression: exp.Func) -> str: 113 """ 114 Trino doesn't support FIRST / LAST as functions, but they're valid in the context 115 of MATCH_RECOGNIZE, so we need to preserve them in that case. In all other cases 116 they're converted into an ARBITRARY call. 117 118 Reference: https://trino.io/docs/current/sql/match-recognize.html#logical-navigation-functions 119 """ 120 if isinstance(expression.find_ancestor(exp.MatchRecognize, exp.Select), exp.MatchRecognize): 121 return self.function_fallback_sql(expression) 122 123 return rename_func("ARBITRARY")(self, expression) 124 125 126def _unix_to_time_sql(self: PrestoGenerator, expression: exp.UnixToTime) -> str: 127 scale = expression.args.get("scale") 128 timestamp = self.sql(expression, "this") 129 if scale in (None, exp.UnixToTime.SECONDS): 130 return rename_func("FROM_UNIXTIME")(self, expression) 131 132 return f"FROM_UNIXTIME(CAST({timestamp} AS DOUBLE) / POW(10, {scale}))" 133 134 135def _to_int(self: PrestoGenerator, expression: exp.Expr) -> exp.Expr: 136 if not expression.type: 137 from sqlglot.optimizer.annotate_types import annotate_types 138 139 annotate_types(expression, dialect=self.dialect) 140 if expression.type and expression.type.this not in exp.DataType.INTEGER_TYPES: 141 return exp.cast(expression, to=exp.DType.BIGINT) 142 return expression 143 144 145def _date_delta_sql( 146 name: str, negate_interval: bool = False 147) -> t.Callable[[PrestoGenerator, DATE_ADD_OR_SUB], str]: 148 def _delta_sql(self: PrestoGenerator, expression: DATE_ADD_OR_SUB) -> str: 149 interval = _to_int(self, expression.expression) 150 return self.func( 151 name, 152 unit_to_str(expression), 153 interval * (-1) if negate_interval else interval, 154 expression.this, 155 ) 156 157 return _delta_sql 158 159 160def _explode_to_unnest_sql(self: PrestoGenerator, expression: exp.Lateral) -> str: 161 explode = expression.this 162 if isinstance(explode, exp.Explode): 163 exploded_type = explode.this.type 164 alias = expression.args.get("alias") 165 166 # This attempts a best-effort transpilation of LATERAL VIEW EXPLODE on a struct array 167 if ( 168 isinstance(alias, exp.TableAlias) 169 and isinstance(exploded_type, exp.DataType) 170 and exploded_type.is_type(exp.DType.ARRAY) 171 and exploded_type.expressions 172 and exploded_type.expressions[0].is_type(exp.DType.STRUCT) 173 ): 174 # When unnesting a ROW in Presto, it produces N columns, so we need to fix the alias 175 alias.set("columns", [c.this.copy() for c in exploded_type.expressions[0].expressions]) 176 elif isinstance(explode, exp.Inline): 177 explode.replace(exp.Explode(this=explode.this.copy())) 178 179 return explode_to_unnest_sql(self, expression) 180 181 182def amend_exploded_column_table(expression: exp.Expr) -> exp.Expr: 183 # We check for expression.type because the columns can be amended only if types were inferred 184 if isinstance(expression, exp.Select) and expression.type: 185 for lateral in expression.args.get("laterals") or []: 186 alias = lateral.args.get("alias") 187 if ( 188 not isinstance(lateral.this, exp.Explode) 189 or not isinstance(alias, exp.TableAlias) 190 or len(alias.columns) != 1 191 ): 192 continue 193 194 new_table = alias.this 195 old_table = alias.columns[0].name.lower() 196 197 # When transpiling a LATERAL VIEW EXPLODE Spark query, the exploded fields may be qualified 198 # with the struct column, resulting in invalid Presto references that need to be amended 199 for column in find_all_in_scope(expression, exp.Column): 200 if column.db.lower() == old_table: 201 column.set("table", column.args["db"].pop()) 202 elif column.table.lower() == old_table: 203 column.set("table", new_table.copy()) 204 elif column.name.lower() == old_table and isinstance(column.parent, exp.Dot): 205 column.parent.replace(exp.column(column.parent.expression, table=new_table)) 206 207 return expression 208 209 210class PrestoGenerator(generator.Generator): 211 SELECT_KINDS: tuple[str, ...] = () 212 SUPPORTS_DECODE_CASE = False 213 214 AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS 215 216 INTERVAL_ALLOWS_PLURAL_FORM = False 217 JOIN_HINTS = False 218 TABLE_HINTS = False 219 QUERY_HINTS = False 220 IS_BOOL_ALLOWED = False 221 TZ_TO_WITH_TIME_ZONE = True 222 NVL2_SUPPORTED = False 223 STRUCT_DELIMITER = ("(", ")") 224 LIMIT_ONLY_LITERALS = True 225 SUPPORTS_SINGLE_ARG_CONCAT = False 226 LIKE_PROPERTY_INSIDE_SCHEMA = True 227 MULTI_ARG_DISTINCT = False 228 SUPPORTS_TO_NUMBER = False 229 HEX_FUNC = "TO_HEX" 230 PARSE_JSON_NAME: str | None = "JSON_PARSE" 231 PAD_FILL_PATTERN_IS_REQUIRED = True 232 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 233 SUPPORTS_MEDIAN = False 234 ARRAY_SIZE_NAME = "CARDINALITY" 235 236 PROPERTIES_LOCATION = { 237 **generator.Generator.PROPERTIES_LOCATION, 238 exp.LocationProperty: exp.Properties.Location.UNSUPPORTED, 239 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 240 } 241 242 TYPE_MAPPING = { 243 **generator.Generator.TYPE_MAPPING, 244 exp.DType.BINARY: "VARBINARY", 245 exp.DType.BIT: "BOOLEAN", 246 exp.DType.DATETIME: "TIMESTAMP", 247 exp.DType.DATETIME64: "TIMESTAMP", 248 exp.DType.FLOAT: "REAL", 249 exp.DType.HLLSKETCH: "HYPERLOGLOG", 250 exp.DType.INT: "INTEGER", 251 exp.DType.STRUCT: "ROW", 252 exp.DType.TEXT: "VARCHAR", 253 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 254 exp.DType.TIMESTAMPNTZ: "TIMESTAMP", 255 exp.DType.TIMETZ: "TIME", 256 } 257 258 TRANSFORMS = { 259 **generator.Generator.TRANSFORMS, 260 exp.AnyValue: rename_func("ARBITRARY"), 261 exp.ApproxQuantile: lambda self, e: self.func( 262 "APPROX_PERCENTILE", 263 e.this, 264 e.args.get("weight"), 265 e.args.get("quantile"), 266 e.args.get("accuracy"), 267 ), 268 exp.ArgMax: rename_func("MAX_BY"), 269 exp.ArgMin: rename_func("MIN_BY"), 270 exp.Array: transforms.preprocess( 271 [transforms.inherit_struct_field_names], 272 generator=lambda self, e: f"ARRAY[{self.expressions(e, flat=True)}]", 273 ), 274 exp.ArrayAny: rename_func("ANY_MATCH"), 275 exp.ArrayConcat: rename_func("CONCAT"), 276 exp.ArrayContains: rename_func("CONTAINS"), 277 exp.ArrayToString: rename_func("ARRAY_JOIN"), 278 exp.ArrayUniqueAgg: rename_func("SET_AGG"), 279 exp.ArraySlice: rename_func("SLICE"), 280 exp.AtTimeZone: rename_func("AT_TIMEZONE"), 281 exp.BitwiseAnd: lambda self, e: self.func("BITWISE_AND", e.this, e.expression), 282 exp.BitwiseLeftShift: lambda self, e: self.func( 283 "BITWISE_ARITHMETIC_SHIFT_LEFT", e.this, e.expression 284 ), 285 exp.BitwiseNot: lambda self, e: self.func("BITWISE_NOT", e.this), 286 exp.BitwiseOr: lambda self, e: self.func("BITWISE_OR", e.this, e.expression), 287 exp.BitwiseRightShift: lambda self, e: self.func( 288 "BITWISE_ARITHMETIC_SHIFT_RIGHT", e.this, e.expression 289 ), 290 exp.BitwiseXor: lambda self, e: self.func("BITWISE_XOR", e.this, e.expression), 291 exp.Cast: transforms.preprocess([transforms.epoch_cast_to_ts]), 292 exp.CurrentTime: lambda *_: "CURRENT_TIME", 293 exp.CurrentTimestamp: lambda *_: "CURRENT_TIMESTAMP", 294 exp.CurrentUser: lambda *_: "CURRENT_USER", 295 exp.DateAdd: _date_delta_sql("DATE_ADD"), 296 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", unit_to_str(e), e.expression, e.this), 297 exp.DateStrToDate: datestrtodate_sql, 298 exp.DateToDi: lambda self, e: ( 299 f"CAST(DATE_FORMAT({self.sql(e, 'this')}, {type(self.dialect).DATEINT_FORMAT}) AS INT)" 300 ), 301 exp.DateSub: _date_delta_sql("DATE_ADD", negate_interval=True), 302 exp.DayOfWeek: lambda self, e: f"(({self.func('DAY_OF_WEEK', e.this)} % 7) + 1)", 303 exp.DayOfWeekIso: rename_func("DAY_OF_WEEK"), 304 exp.Decode: lambda self, e: encode_decode_sql(self, e, "FROM_UTF8"), 305 exp.DiToDate: lambda self, e: ( 306 f"CAST(DATE_PARSE(CAST({self.sql(e, 'this')} AS VARCHAR), {type(self.dialect).DATEINT_FORMAT}) AS DATE)" 307 ), 308 exp.Encode: lambda self, e: encode_decode_sql(self, e, "TO_UTF8"), 309 exp.FileFormatProperty: lambda self, e: f"format={self.sql(exp.Literal.string(e.name))}", 310 exp.First: _first_last_sql, 311 exp.FromTimeZone: lambda self, e: ( 312 f"WITH_TIMEZONE({self.sql(e, 'this')}, {self.sql(e, 'zone')}) AT TIME ZONE 'UTC'" 313 ), 314 exp.GenerateSeries: sequence_sql, 315 exp.GenerateDateArray: sequence_sql, 316 exp.If: if_sql(), 317 exp.ILike: no_ilike_sql, 318 exp.Initcap: _initcap_sql, 319 exp.Last: _first_last_sql, 320 exp.LastDay: lambda self, e: self.func("LAST_DAY_OF_MONTH", e.this), 321 exp.Lateral: _explode_to_unnest_sql, 322 exp.Left: left_to_substring_sql, 323 exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")( 324 rename_func("LEVENSHTEIN_DISTANCE") 325 ), 326 exp.LogicalAnd: rename_func("BOOL_AND"), 327 exp.LogicalOr: rename_func("BOOL_OR"), 328 exp.Pivot: no_pivot_sql, 329 exp.Quantile: _quantile_sql, 330 exp.RegexpExtract: regexp_extract_sql, 331 exp.RegexpExtractAll: regexp_extract_sql, 332 exp.Right: right_to_substring_sql, 333 exp.Schema: _schema_sql, 334 exp.SchemaCommentProperty: lambda self, e: self.naked_property(e), 335 exp.Select: transforms.preprocess( 336 [ 337 transforms.eliminate_window_clause, 338 transforms.eliminate_qualify, 339 transforms.eliminate_distinct_on, 340 transforms.explode_projection_to_unnest(1), 341 transforms.eliminate_semi_and_anti_joins, 342 amend_exploded_column_table, 343 ] 344 ), 345 exp.SortArray: _no_sort_array, 346 exp.SqlSecurityProperty: lambda self, e: f"SECURITY {self.sql(e.this)}", 347 exp.StrPosition: lambda self, e: strposition_sql(self, e, supports_occurrence=True), 348 exp.StrToDate: lambda self, e: f"CAST({_str_to_time_sql(self, e)} AS DATE)", 349 exp.StrToMap: rename_func("SPLIT_TO_MAP"), 350 exp.StrToTime: _str_to_time_sql, 351 exp.StructExtract: struct_extract_sql, 352 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 353 exp.Timestamp: no_timestamp_sql, 354 exp.TimestampAdd: _date_delta_sql("DATE_ADD"), 355 exp.TimestampTrunc: timestamptrunc_sql(), 356 exp.TimeStrToDate: timestrtotime_sql, 357 exp.TimeStrToTime: timestrtotime_sql, 358 exp.TimeStrToUnix: lambda self, e: self.func( 359 "TO_UNIXTIME", self.func("DATE_PARSE", e.this, type(self.dialect).TIME_FORMAT) 360 ), 361 exp.TimeToStr: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 362 exp.TimeToUnix: rename_func("TO_UNIXTIME"), 363 exp.ToChar: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 364 exp.TryCast: transforms.preprocess([transforms.epoch_cast_to_ts]), 365 exp.TsOrDiToDi: lambda self, e: ( 366 f"CAST(SUBSTR(REPLACE(CAST({self.sql(e, 'this')} AS VARCHAR), '-', ''), 1, 8) AS INT)" 367 ), 368 exp.TsOrDsAdd: _ts_or_ds_add_sql, 369 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 370 exp.TsOrDsToDate: _ts_or_ds_to_date_sql, 371 exp.Unhex: rename_func("FROM_HEX"), 372 exp.UnixToStr: lambda self, e: ( 373 f"DATE_FORMAT(FROM_UNIXTIME({self.sql(e, 'this')}), {self.format_time(e)})" 374 ), 375 exp.UnixToTime: _unix_to_time_sql, 376 exp.UnixToTimeStr: lambda self, e: f"CAST(FROM_UNIXTIME({self.sql(e, 'this')}) AS VARCHAR)", 377 exp.VariancePop: rename_func("VAR_POP"), 378 exp.With: transforms.preprocess([transforms.add_recursive_cte_column_names]), 379 exp.WithinGroup: transforms.preprocess([transforms.remove_within_group_for_percentiles]), 380 # Note: Presto's TRUNCATE always returns DOUBLE, even with decimals=0, whereas 381 # most dialects return INT (SQLite also returns REAL, see sqlite.py). This creates 382 # a bidirectional transpilation gap: Presto→Other may change float division to int 383 # division, and vice versa. Modeling precisely would require exp.FloatTrunc or 384 # similar, deemed overengineering for this subtle semantic difference. 385 exp.Trunc: rename_func("TRUNCATE"), 386 exp.Xor: bool_xor_sql, 387 exp.MD5Digest: rename_func("MD5"), 388 exp.SHA: rename_func("SHA1"), 389 exp.SHA1Digest: rename_func("SHA1"), 390 exp.SHA2: sha256_sql, 391 exp.SHA2Digest: sha2_digest_sql, 392 } 393 394 RESERVED_KEYWORDS = { 395 "alter", 396 "and", 397 "as", 398 "between", 399 "by", 400 "case", 401 "cast", 402 "constraint", 403 "create", 404 "cross", 405 "current_time", 406 "current_timestamp", 407 "deallocate", 408 "delete", 409 "describe", 410 "distinct", 411 "drop", 412 "else", 413 "end", 414 "escape", 415 "except", 416 "execute", 417 "exists", 418 "extract", 419 "false", 420 "for", 421 "from", 422 "full", 423 "group", 424 "having", 425 "in", 426 "inner", 427 "insert", 428 "intersect", 429 "into", 430 "is", 431 "join", 432 "left", 433 "like", 434 "natural", 435 "not", 436 "null", 437 "on", 438 "or", 439 "order", 440 "outer", 441 "prepare", 442 "right", 443 "select", 444 "table", 445 "then", 446 "true", 447 "union", 448 "using", 449 "values", 450 "when", 451 "where", 452 "with", 453 } 454 455 def extract_sql(self, expression: exp.Extract) -> str: 456 date_part = expression.name 457 458 if not date_part.startswith("EPOCH"): 459 return super().extract_sql(expression) 460 461 if date_part == "EPOCH_MILLISECOND": 462 scale = 10**3 463 elif date_part == "EPOCH_MICROSECOND": 464 scale = 10**6 465 elif date_part == "EPOCH_NANOSECOND": 466 scale = 10**9 467 else: 468 scale = None 469 470 value = expression.expression 471 472 ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr()) 473 to_unix: exp.Expr = exp.TimeToUnix(this=ts) 474 475 if scale: 476 to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale)) 477 478 return self.sql(to_unix) 479 480 def jsonformat_sql(self, expression: exp.JSONFormat) -> str: 481 this = expression.this 482 is_json = expression.args.get("is_json") 483 484 if this and not (is_json or this.type): 485 from sqlglot.optimizer.annotate_types import annotate_types 486 487 this = annotate_types(this, dialect=self.dialect) 488 489 if not (is_json or this.is_type(exp.DType.JSON)): 490 this.replace(exp.cast(this, exp.DType.JSON)) 491 492 return self.function_fallback_sql(expression) 493 494 def md5_sql(self, expression: exp.MD5) -> str: 495 this = expression.this 496 497 if not this.type: 498 from sqlglot.optimizer.annotate_types import annotate_types 499 500 this = annotate_types(this, dialect=self.dialect) 501 502 if this.is_type(*exp.DataType.TEXT_TYPES): 503 this = exp.Encode(this=this, charset=exp.Literal.string("utf-8")) 504 505 return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this)))) 506 507 def strtounix_sql(self, expression: exp.StrToUnix) -> str: 508 # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one. 509 # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a 510 # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback, 511 # which seems to be using the same time mapping as Hive, as per: 512 # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html 513 this = expression.this 514 value_as_text = exp.cast(this, exp.DType.TEXT) 515 value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this 516 517 parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression)) 518 519 formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression)) 520 parse_with_tz = self.func( 521 "PARSE_DATETIME", 522 formatted_value, 523 self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE), 524 ) 525 coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz) 526 return self.func("TO_UNIXTIME", coalesced) 527 528 def bracket_sql(self, expression: exp.Bracket) -> str: 529 if expression.args.get("safe"): 530 return bracket_to_element_at_sql(self, expression) 531 return super().bracket_sql(expression) 532 533 def struct_sql(self, expression: exp.Struct) -> str: 534 if not expression.type: 535 from sqlglot.optimizer.annotate_types import annotate_types 536 537 annotate_types(expression, dialect=self.dialect) 538 539 values: list[str] = [] 540 schema: list[str] = [] 541 unknown_type = False 542 543 for e in expression.expressions: 544 if isinstance(e, exp.PropertyEQ): 545 if e.type and e.type.is_type(exp.DType.UNKNOWN): 546 unknown_type = True 547 else: 548 schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}") 549 values.append(self.sql(e, "expression")) 550 else: 551 values.append(self.sql(e)) 552 553 size = len(expression.expressions) 554 555 if not size or len(schema) != size: 556 if unknown_type: 557 self.unsupported( 558 "Cannot convert untyped key-value definitions (try annotate_types)." 559 ) 560 return self.func("ROW", *values) 561 return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))" 562 563 def interval_sql(self, expression: exp.Interval) -> str: 564 if expression.this and expression.text("unit").upper().startswith("WEEK"): 565 return f"({expression.this.name} * INTERVAL '7' DAY)" 566 return super().interval_sql(expression) 567 568 def transaction_sql(self, expression: exp.Transaction) -> str: 569 modes = expression.args.get("modes") 570 modes = f" {', '.join(modes)}" if modes else "" 571 return f"START TRANSACTION{modes}" 572 573 def offset_limit_modifiers( 574 self, expression: exp.Expr, fetch: bool, limit: exp.Fetch | exp.Limit | None 575 ) -> list[str]: 576 return [ 577 self.sql(expression, "offset"), 578 self.sql(limit), 579 ] 580 581 def create_sql(self, expression: exp.Create) -> str: 582 """ 583 Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression), 584 so we need to remove them 585 """ 586 kind = expression.args["kind"] 587 schema = expression.this 588 if kind == "VIEW" and schema.expressions: 589 expression.this.set("expressions", None) 590 return super().create_sql(expression) 591 592 def delete_sql(self, expression: exp.Delete) -> str: 593 """ 594 Presto only supports DELETE FROM for a single table without an alias, so we need 595 to remove the unnecessary parts. If the original DELETE statement contains more 596 than one table to be deleted, we can't safely map it 1-1 to a Presto statement. 597 """ 598 tables = expression.args.get("tables") or [expression.this] 599 if len(tables) > 1: 600 return super().delete_sql(expression) 601 602 table = tables[0] 603 expression.set("this", table) 604 expression.set("tables", None) 605 606 if isinstance(table, exp.Table): 607 table_alias = table.args.get("alias") 608 if table_alias: 609 table_alias.pop() 610 expression = t.cast(exp.Delete, expression.transform(unqualify_columns)) 611 612 return super().delete_sql(expression) 613 614 def jsonextract_sql(self, expression: exp.JSONExtract) -> str: 615 is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True) 616 617 # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks 618 # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino 619 if not expression.args.get("variant_extract") or is_json_extract: 620 return self.func( 621 "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions 622 ) 623 624 this = self.sql(expression, "this") 625 626 # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y 627 segments = [] 628 for path_key in expression.expression.expressions[1:]: 629 if not isinstance(path_key, exp.JSONPathKey): 630 # Cannot transpile subscripts, wildcards etc to dot notation 631 self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access") 632 continue 633 key = path_key.this 634 if not exp.SAFE_IDENTIFIER_RE.match(key): 635 key = f'"{key}"' 636 segments.append(f".{key}") 637 638 expr = "".join(segments) 639 640 return f"{this}{expr}" 641 642 def groupconcat_sql(self, expression: exp.GroupConcat) -> str: 643 return self.func( 644 "ARRAY_JOIN", 645 self.func("ARRAY_AGG", expression.this), 646 expression.args.get("separator"), 647 )
DATE_ADD_OR_SUB =
typing.Union[sqlglot.expressions.temporal.DateAdd, sqlglot.expressions.temporal.TimestampAdd, sqlglot.expressions.temporal.DateSub]
def
amend_exploded_column_table( expression: sqlglot.expressions.core.Expr) -> sqlglot.expressions.core.Expr:
183def amend_exploded_column_table(expression: exp.Expr) -> exp.Expr: 184 # We check for expression.type because the columns can be amended only if types were inferred 185 if isinstance(expression, exp.Select) and expression.type: 186 for lateral in expression.args.get("laterals") or []: 187 alias = lateral.args.get("alias") 188 if ( 189 not isinstance(lateral.this, exp.Explode) 190 or not isinstance(alias, exp.TableAlias) 191 or len(alias.columns) != 1 192 ): 193 continue 194 195 new_table = alias.this 196 old_table = alias.columns[0].name.lower() 197 198 # When transpiling a LATERAL VIEW EXPLODE Spark query, the exploded fields may be qualified 199 # with the struct column, resulting in invalid Presto references that need to be amended 200 for column in find_all_in_scope(expression, exp.Column): 201 if column.db.lower() == old_table: 202 column.set("table", column.args["db"].pop()) 203 elif column.table.lower() == old_table: 204 column.set("table", new_table.copy()) 205 elif column.name.lower() == old_table and isinstance(column.parent, exp.Dot): 206 column.parent.replace(exp.column(column.parent.expression, table=new_table)) 207 208 return expression
211class PrestoGenerator(generator.Generator): 212 SELECT_KINDS: tuple[str, ...] = () 213 SUPPORTS_DECODE_CASE = False 214 215 AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS 216 217 INTERVAL_ALLOWS_PLURAL_FORM = False 218 JOIN_HINTS = False 219 TABLE_HINTS = False 220 QUERY_HINTS = False 221 IS_BOOL_ALLOWED = False 222 TZ_TO_WITH_TIME_ZONE = True 223 NVL2_SUPPORTED = False 224 STRUCT_DELIMITER = ("(", ")") 225 LIMIT_ONLY_LITERALS = True 226 SUPPORTS_SINGLE_ARG_CONCAT = False 227 LIKE_PROPERTY_INSIDE_SCHEMA = True 228 MULTI_ARG_DISTINCT = False 229 SUPPORTS_TO_NUMBER = False 230 HEX_FUNC = "TO_HEX" 231 PARSE_JSON_NAME: str | None = "JSON_PARSE" 232 PAD_FILL_PATTERN_IS_REQUIRED = True 233 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 234 SUPPORTS_MEDIAN = False 235 ARRAY_SIZE_NAME = "CARDINALITY" 236 237 PROPERTIES_LOCATION = { 238 **generator.Generator.PROPERTIES_LOCATION, 239 exp.LocationProperty: exp.Properties.Location.UNSUPPORTED, 240 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 241 } 242 243 TYPE_MAPPING = { 244 **generator.Generator.TYPE_MAPPING, 245 exp.DType.BINARY: "VARBINARY", 246 exp.DType.BIT: "BOOLEAN", 247 exp.DType.DATETIME: "TIMESTAMP", 248 exp.DType.DATETIME64: "TIMESTAMP", 249 exp.DType.FLOAT: "REAL", 250 exp.DType.HLLSKETCH: "HYPERLOGLOG", 251 exp.DType.INT: "INTEGER", 252 exp.DType.STRUCT: "ROW", 253 exp.DType.TEXT: "VARCHAR", 254 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 255 exp.DType.TIMESTAMPNTZ: "TIMESTAMP", 256 exp.DType.TIMETZ: "TIME", 257 } 258 259 TRANSFORMS = { 260 **generator.Generator.TRANSFORMS, 261 exp.AnyValue: rename_func("ARBITRARY"), 262 exp.ApproxQuantile: lambda self, e: self.func( 263 "APPROX_PERCENTILE", 264 e.this, 265 e.args.get("weight"), 266 e.args.get("quantile"), 267 e.args.get("accuracy"), 268 ), 269 exp.ArgMax: rename_func("MAX_BY"), 270 exp.ArgMin: rename_func("MIN_BY"), 271 exp.Array: transforms.preprocess( 272 [transforms.inherit_struct_field_names], 273 generator=lambda self, e: f"ARRAY[{self.expressions(e, flat=True)}]", 274 ), 275 exp.ArrayAny: rename_func("ANY_MATCH"), 276 exp.ArrayConcat: rename_func("CONCAT"), 277 exp.ArrayContains: rename_func("CONTAINS"), 278 exp.ArrayToString: rename_func("ARRAY_JOIN"), 279 exp.ArrayUniqueAgg: rename_func("SET_AGG"), 280 exp.ArraySlice: rename_func("SLICE"), 281 exp.AtTimeZone: rename_func("AT_TIMEZONE"), 282 exp.BitwiseAnd: lambda self, e: self.func("BITWISE_AND", e.this, e.expression), 283 exp.BitwiseLeftShift: lambda self, e: self.func( 284 "BITWISE_ARITHMETIC_SHIFT_LEFT", e.this, e.expression 285 ), 286 exp.BitwiseNot: lambda self, e: self.func("BITWISE_NOT", e.this), 287 exp.BitwiseOr: lambda self, e: self.func("BITWISE_OR", e.this, e.expression), 288 exp.BitwiseRightShift: lambda self, e: self.func( 289 "BITWISE_ARITHMETIC_SHIFT_RIGHT", e.this, e.expression 290 ), 291 exp.BitwiseXor: lambda self, e: self.func("BITWISE_XOR", e.this, e.expression), 292 exp.Cast: transforms.preprocess([transforms.epoch_cast_to_ts]), 293 exp.CurrentTime: lambda *_: "CURRENT_TIME", 294 exp.CurrentTimestamp: lambda *_: "CURRENT_TIMESTAMP", 295 exp.CurrentUser: lambda *_: "CURRENT_USER", 296 exp.DateAdd: _date_delta_sql("DATE_ADD"), 297 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", unit_to_str(e), e.expression, e.this), 298 exp.DateStrToDate: datestrtodate_sql, 299 exp.DateToDi: lambda self, e: ( 300 f"CAST(DATE_FORMAT({self.sql(e, 'this')}, {type(self.dialect).DATEINT_FORMAT}) AS INT)" 301 ), 302 exp.DateSub: _date_delta_sql("DATE_ADD", negate_interval=True), 303 exp.DayOfWeek: lambda self, e: f"(({self.func('DAY_OF_WEEK', e.this)} % 7) + 1)", 304 exp.DayOfWeekIso: rename_func("DAY_OF_WEEK"), 305 exp.Decode: lambda self, e: encode_decode_sql(self, e, "FROM_UTF8"), 306 exp.DiToDate: lambda self, e: ( 307 f"CAST(DATE_PARSE(CAST({self.sql(e, 'this')} AS VARCHAR), {type(self.dialect).DATEINT_FORMAT}) AS DATE)" 308 ), 309 exp.Encode: lambda self, e: encode_decode_sql(self, e, "TO_UTF8"), 310 exp.FileFormatProperty: lambda self, e: f"format={self.sql(exp.Literal.string(e.name))}", 311 exp.First: _first_last_sql, 312 exp.FromTimeZone: lambda self, e: ( 313 f"WITH_TIMEZONE({self.sql(e, 'this')}, {self.sql(e, 'zone')}) AT TIME ZONE 'UTC'" 314 ), 315 exp.GenerateSeries: sequence_sql, 316 exp.GenerateDateArray: sequence_sql, 317 exp.If: if_sql(), 318 exp.ILike: no_ilike_sql, 319 exp.Initcap: _initcap_sql, 320 exp.Last: _first_last_sql, 321 exp.LastDay: lambda self, e: self.func("LAST_DAY_OF_MONTH", e.this), 322 exp.Lateral: _explode_to_unnest_sql, 323 exp.Left: left_to_substring_sql, 324 exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")( 325 rename_func("LEVENSHTEIN_DISTANCE") 326 ), 327 exp.LogicalAnd: rename_func("BOOL_AND"), 328 exp.LogicalOr: rename_func("BOOL_OR"), 329 exp.Pivot: no_pivot_sql, 330 exp.Quantile: _quantile_sql, 331 exp.RegexpExtract: regexp_extract_sql, 332 exp.RegexpExtractAll: regexp_extract_sql, 333 exp.Right: right_to_substring_sql, 334 exp.Schema: _schema_sql, 335 exp.SchemaCommentProperty: lambda self, e: self.naked_property(e), 336 exp.Select: transforms.preprocess( 337 [ 338 transforms.eliminate_window_clause, 339 transforms.eliminate_qualify, 340 transforms.eliminate_distinct_on, 341 transforms.explode_projection_to_unnest(1), 342 transforms.eliminate_semi_and_anti_joins, 343 amend_exploded_column_table, 344 ] 345 ), 346 exp.SortArray: _no_sort_array, 347 exp.SqlSecurityProperty: lambda self, e: f"SECURITY {self.sql(e.this)}", 348 exp.StrPosition: lambda self, e: strposition_sql(self, e, supports_occurrence=True), 349 exp.StrToDate: lambda self, e: f"CAST({_str_to_time_sql(self, e)} AS DATE)", 350 exp.StrToMap: rename_func("SPLIT_TO_MAP"), 351 exp.StrToTime: _str_to_time_sql, 352 exp.StructExtract: struct_extract_sql, 353 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 354 exp.Timestamp: no_timestamp_sql, 355 exp.TimestampAdd: _date_delta_sql("DATE_ADD"), 356 exp.TimestampTrunc: timestamptrunc_sql(), 357 exp.TimeStrToDate: timestrtotime_sql, 358 exp.TimeStrToTime: timestrtotime_sql, 359 exp.TimeStrToUnix: lambda self, e: self.func( 360 "TO_UNIXTIME", self.func("DATE_PARSE", e.this, type(self.dialect).TIME_FORMAT) 361 ), 362 exp.TimeToStr: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 363 exp.TimeToUnix: rename_func("TO_UNIXTIME"), 364 exp.ToChar: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 365 exp.TryCast: transforms.preprocess([transforms.epoch_cast_to_ts]), 366 exp.TsOrDiToDi: lambda self, e: ( 367 f"CAST(SUBSTR(REPLACE(CAST({self.sql(e, 'this')} AS VARCHAR), '-', ''), 1, 8) AS INT)" 368 ), 369 exp.TsOrDsAdd: _ts_or_ds_add_sql, 370 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 371 exp.TsOrDsToDate: _ts_or_ds_to_date_sql, 372 exp.Unhex: rename_func("FROM_HEX"), 373 exp.UnixToStr: lambda self, e: ( 374 f"DATE_FORMAT(FROM_UNIXTIME({self.sql(e, 'this')}), {self.format_time(e)})" 375 ), 376 exp.UnixToTime: _unix_to_time_sql, 377 exp.UnixToTimeStr: lambda self, e: f"CAST(FROM_UNIXTIME({self.sql(e, 'this')}) AS VARCHAR)", 378 exp.VariancePop: rename_func("VAR_POP"), 379 exp.With: transforms.preprocess([transforms.add_recursive_cte_column_names]), 380 exp.WithinGroup: transforms.preprocess([transforms.remove_within_group_for_percentiles]), 381 # Note: Presto's TRUNCATE always returns DOUBLE, even with decimals=0, whereas 382 # most dialects return INT (SQLite also returns REAL, see sqlite.py). This creates 383 # a bidirectional transpilation gap: Presto→Other may change float division to int 384 # division, and vice versa. Modeling precisely would require exp.FloatTrunc or 385 # similar, deemed overengineering for this subtle semantic difference. 386 exp.Trunc: rename_func("TRUNCATE"), 387 exp.Xor: bool_xor_sql, 388 exp.MD5Digest: rename_func("MD5"), 389 exp.SHA: rename_func("SHA1"), 390 exp.SHA1Digest: rename_func("SHA1"), 391 exp.SHA2: sha256_sql, 392 exp.SHA2Digest: sha2_digest_sql, 393 } 394 395 RESERVED_KEYWORDS = { 396 "alter", 397 "and", 398 "as", 399 "between", 400 "by", 401 "case", 402 "cast", 403 "constraint", 404 "create", 405 "cross", 406 "current_time", 407 "current_timestamp", 408 "deallocate", 409 "delete", 410 "describe", 411 "distinct", 412 "drop", 413 "else", 414 "end", 415 "escape", 416 "except", 417 "execute", 418 "exists", 419 "extract", 420 "false", 421 "for", 422 "from", 423 "full", 424 "group", 425 "having", 426 "in", 427 "inner", 428 "insert", 429 "intersect", 430 "into", 431 "is", 432 "join", 433 "left", 434 "like", 435 "natural", 436 "not", 437 "null", 438 "on", 439 "or", 440 "order", 441 "outer", 442 "prepare", 443 "right", 444 "select", 445 "table", 446 "then", 447 "true", 448 "union", 449 "using", 450 "values", 451 "when", 452 "where", 453 "with", 454 } 455 456 def extract_sql(self, expression: exp.Extract) -> str: 457 date_part = expression.name 458 459 if not date_part.startswith("EPOCH"): 460 return super().extract_sql(expression) 461 462 if date_part == "EPOCH_MILLISECOND": 463 scale = 10**3 464 elif date_part == "EPOCH_MICROSECOND": 465 scale = 10**6 466 elif date_part == "EPOCH_NANOSECOND": 467 scale = 10**9 468 else: 469 scale = None 470 471 value = expression.expression 472 473 ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr()) 474 to_unix: exp.Expr = exp.TimeToUnix(this=ts) 475 476 if scale: 477 to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale)) 478 479 return self.sql(to_unix) 480 481 def jsonformat_sql(self, expression: exp.JSONFormat) -> str: 482 this = expression.this 483 is_json = expression.args.get("is_json") 484 485 if this and not (is_json or this.type): 486 from sqlglot.optimizer.annotate_types import annotate_types 487 488 this = annotate_types(this, dialect=self.dialect) 489 490 if not (is_json or this.is_type(exp.DType.JSON)): 491 this.replace(exp.cast(this, exp.DType.JSON)) 492 493 return self.function_fallback_sql(expression) 494 495 def md5_sql(self, expression: exp.MD5) -> str: 496 this = expression.this 497 498 if not this.type: 499 from sqlglot.optimizer.annotate_types import annotate_types 500 501 this = annotate_types(this, dialect=self.dialect) 502 503 if this.is_type(*exp.DataType.TEXT_TYPES): 504 this = exp.Encode(this=this, charset=exp.Literal.string("utf-8")) 505 506 return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this)))) 507 508 def strtounix_sql(self, expression: exp.StrToUnix) -> str: 509 # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one. 510 # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a 511 # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback, 512 # which seems to be using the same time mapping as Hive, as per: 513 # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html 514 this = expression.this 515 value_as_text = exp.cast(this, exp.DType.TEXT) 516 value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this 517 518 parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression)) 519 520 formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression)) 521 parse_with_tz = self.func( 522 "PARSE_DATETIME", 523 formatted_value, 524 self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE), 525 ) 526 coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz) 527 return self.func("TO_UNIXTIME", coalesced) 528 529 def bracket_sql(self, expression: exp.Bracket) -> str: 530 if expression.args.get("safe"): 531 return bracket_to_element_at_sql(self, expression) 532 return super().bracket_sql(expression) 533 534 def struct_sql(self, expression: exp.Struct) -> str: 535 if not expression.type: 536 from sqlglot.optimizer.annotate_types import annotate_types 537 538 annotate_types(expression, dialect=self.dialect) 539 540 values: list[str] = [] 541 schema: list[str] = [] 542 unknown_type = False 543 544 for e in expression.expressions: 545 if isinstance(e, exp.PropertyEQ): 546 if e.type and e.type.is_type(exp.DType.UNKNOWN): 547 unknown_type = True 548 else: 549 schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}") 550 values.append(self.sql(e, "expression")) 551 else: 552 values.append(self.sql(e)) 553 554 size = len(expression.expressions) 555 556 if not size or len(schema) != size: 557 if unknown_type: 558 self.unsupported( 559 "Cannot convert untyped key-value definitions (try annotate_types)." 560 ) 561 return self.func("ROW", *values) 562 return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))" 563 564 def interval_sql(self, expression: exp.Interval) -> str: 565 if expression.this and expression.text("unit").upper().startswith("WEEK"): 566 return f"({expression.this.name} * INTERVAL '7' DAY)" 567 return super().interval_sql(expression) 568 569 def transaction_sql(self, expression: exp.Transaction) -> str: 570 modes = expression.args.get("modes") 571 modes = f" {', '.join(modes)}" if modes else "" 572 return f"START TRANSACTION{modes}" 573 574 def offset_limit_modifiers( 575 self, expression: exp.Expr, fetch: bool, limit: exp.Fetch | exp.Limit | None 576 ) -> list[str]: 577 return [ 578 self.sql(expression, "offset"), 579 self.sql(limit), 580 ] 581 582 def create_sql(self, expression: exp.Create) -> str: 583 """ 584 Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression), 585 so we need to remove them 586 """ 587 kind = expression.args["kind"] 588 schema = expression.this 589 if kind == "VIEW" and schema.expressions: 590 expression.this.set("expressions", None) 591 return super().create_sql(expression) 592 593 def delete_sql(self, expression: exp.Delete) -> str: 594 """ 595 Presto only supports DELETE FROM for a single table without an alias, so we need 596 to remove the unnecessary parts. If the original DELETE statement contains more 597 than one table to be deleted, we can't safely map it 1-1 to a Presto statement. 598 """ 599 tables = expression.args.get("tables") or [expression.this] 600 if len(tables) > 1: 601 return super().delete_sql(expression) 602 603 table = tables[0] 604 expression.set("this", table) 605 expression.set("tables", None) 606 607 if isinstance(table, exp.Table): 608 table_alias = table.args.get("alias") 609 if table_alias: 610 table_alias.pop() 611 expression = t.cast(exp.Delete, expression.transform(unqualify_columns)) 612 613 return super().delete_sql(expression) 614 615 def jsonextract_sql(self, expression: exp.JSONExtract) -> str: 616 is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True) 617 618 # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks 619 # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino 620 if not expression.args.get("variant_extract") or is_json_extract: 621 return self.func( 622 "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions 623 ) 624 625 this = self.sql(expression, "this") 626 627 # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y 628 segments = [] 629 for path_key in expression.expression.expressions[1:]: 630 if not isinstance(path_key, exp.JSONPathKey): 631 # Cannot transpile subscripts, wildcards etc to dot notation 632 self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access") 633 continue 634 key = path_key.this 635 if not exp.SAFE_IDENTIFIER_RE.match(key): 636 key = f'"{key}"' 637 segments.append(f".{key}") 638 639 expr = "".join(segments) 640 641 return f"{this}{expr}" 642 643 def groupconcat_sql(self, expression: exp.GroupConcat) -> str: 644 return self.func( 645 "ARRAY_JOIN", 646 self.func("ARRAY_AGG", expression.this), 647 expression.args.get("separator"), 648 )
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
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.properties.AllowedValuesProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AlgorithmProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApiProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApplicationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.AutoIncrementProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AutoRefreshProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BackupProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BlockCompressionProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CatalogProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ChecksumProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CollateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ComputeProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.query.Cluster'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ClusteredByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DistributedByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DuplicateKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DataBlocksizeProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.DatabaseProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DataDeletionProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DefinerProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DictRange'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DynamicProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DistKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DistStyleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EmptyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EncodeProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.EngineProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EnviromentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.HandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ParameterStyleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ExecuteAsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ExternalProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.FallbackProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.FileFormatProperty'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.FreespaceProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.GlobalProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.HeapProperty'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.HybridProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.InheritsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IcebergProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.IncludeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.InputModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IsolatedLoadingProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.JournalProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.LanguageProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LikeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LocationProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.PartitionedOfProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.constraints.PrimaryKey'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Property'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.RefreshTriggerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RollupProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowAccessProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowFormatProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatDelimitedProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatSerdeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SampleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SchemaCommentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SecureProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SecurityIntegrationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SerdeProperties'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.Set'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SettingsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SetProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SetConfigProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SharingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.SequenceProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.TriggerProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.SortKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlReadWriteProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlSecurityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StabilityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StorageHandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StreamingTableProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.StrictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Tags'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.TemporaryProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ToTableProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.TransientProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.TransformModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.MergeTreeTTL'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.UnloggedProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.UsingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.UsingTemplateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ViewAttributeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.VirtualProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.VolatileProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.WithDataProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.WithJournalTableProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.WithProcedureOptions'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSchemaBindingProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSystemVersioningProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ForceProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>}
TYPE_MAPPING =
{<DType.DATETIME2: 'DATETIME2'>: 'TIMESTAMP', <DType.NCHAR: 'NCHAR'>: 'CHAR', <DType.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <DType.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <DType.LONGTEXT: 'LONGTEXT'>: 'TEXT', <DType.TINYTEXT: 'TINYTEXT'>: 'TEXT', <DType.BLOB: 'BLOB'>: 'VARBINARY', <DType.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <DType.LONGBLOB: 'LONGBLOB'>: 'BLOB', <DType.TINYBLOB: 'TINYBLOB'>: 'BLOB', <DType.INET: 'INET'>: 'INET', <DType.ROWVERSION: 'ROWVERSION'>: 'VARBINARY', <DType.SMALLDATETIME: 'SMALLDATETIME'>: 'TIMESTAMP', <DType.BINARY: 'BINARY'>: 'VARBINARY', <DType.BIT: 'BIT'>: 'BOOLEAN', <DType.DATETIME: 'DATETIME'>: 'TIMESTAMP', <DType.DATETIME64: 'DATETIME64'>: 'TIMESTAMP', <DType.FLOAT: 'FLOAT'>: 'REAL', <DType.HLLSKETCH: 'HLLSKETCH'>: 'HYPERLOGLOG', <DType.INT: 'INT'>: 'INTEGER', <DType.STRUCT: 'STRUCT'>: 'ROW', <DType.TEXT: 'TEXT'>: 'VARCHAR', <DType.TIMESTAMPTZ: 'TIMESTAMPTZ'>: 'TIMESTAMP', <DType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>: 'TIMESTAMP', <DType.TIMETZ: 'TIMETZ'>: 'TIME'}
TRANSFORMS =
{<class 'sqlglot.expressions.query.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.query.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.core.Adjacent'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.AnalyzeColumns'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.AnalyzeWith'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.ArrayContainsAll'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.ArrayOverlaps'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.AssumeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.math.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.string.ConvertToCharset'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CredentialsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.CurrentCatalog'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.SessionUser'>: <function Generator.<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 Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBContainsAnyTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBContainsAllTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBDeleteAtPath'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONObject'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONObjectAgg'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.MaskingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.NetFunc'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.NetworkProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.ExtendsLeft'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.ExtendsRight'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionedByBucket'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionByTruncate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.PositionalColumn'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.InvisibleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ZeroFillColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Put'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <function Generator.<lambda>>, <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 PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.properties.StabilityProperty'>: <function Generator.<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.aggregate.AnyValue'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.ApproxQuantile'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.ArgMax'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.ArgMin'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.array.Array'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.array.ArrayAny'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.array.ArrayConcat'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.array.ArrayContains'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.array.ArrayToString'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.ArrayUniqueAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.array.ArraySlice'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.AtTimeZone'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.BitwiseAnd'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseLeftShift'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseNot'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseOr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseRightShift'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseXor'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.functions.Cast'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.CurrentTime'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.CurrentTimestamp'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.functions.CurrentUser'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateAdd'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.DateDiff'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateStrToDate'>: <function datestrtodate_sql>, <class 'sqlglot.expressions.temporal.DateToDi'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateSub'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.DayOfWeek'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DayOfWeekIso'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Decode'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DiToDate'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.string.Encode'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.properties.FileFormatProperty'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.First'>: <function _first_last_sql>, <class 'sqlglot.expressions.core.FromTimeZone'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.array.GenerateSeries'>: <function sequence_sql>, <class 'sqlglot.expressions.temporal.GenerateDateArray'>: <function sequence_sql>, <class 'sqlglot.expressions.functions.If'>: <function if_sql.<locals>._if_sql>, <class 'sqlglot.expressions.core.ILike'>: <function no_ilike_sql>, <class 'sqlglot.expressions.string.Initcap'>: <function _initcap_sql>, <class 'sqlglot.expressions.aggregate.Last'>: <function _first_last_sql>, <class 'sqlglot.expressions.temporal.LastDay'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.query.Lateral'>: <function _explode_to_unnest_sql>, <class 'sqlglot.expressions.string.Left'>: <function left_to_substring_sql>, <class 'sqlglot.expressions.string.Levenshtein'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.LogicalAnd'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.LogicalOr'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.Pivot'>: <function no_pivot_sql>, <class 'sqlglot.expressions.aggregate.Quantile'>: <function _quantile_sql>, <class 'sqlglot.expressions.string.RegexpExtract'>: <function regexp_extract_sql>, <class 'sqlglot.expressions.string.RegexpExtractAll'>: <function regexp_extract_sql>, <class 'sqlglot.expressions.string.Right'>: <function right_to_substring_sql>, <class 'sqlglot.expressions.query.Schema'>: <function _schema_sql>, <class 'sqlglot.expressions.properties.SchemaCommentProperty'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.query.Select'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.array.SortArray'>: <function _no_sort_array>, <class 'sqlglot.expressions.string.StrPosition'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.StrToDate'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.string.StrToMap'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.StrToTime'>: <function _str_to_time_sql>, <class 'sqlglot.expressions.array.StructExtract'>: <function struct_extract_sql>, <class 'sqlglot.expressions.query.Table'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.Timestamp'>: <function no_timestamp_sql>, <class 'sqlglot.expressions.temporal.TimestampAdd'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.TimestampTrunc'>: <function timestamptrunc_sql.<locals>._timestamptrunc_sql>, <class 'sqlglot.expressions.temporal.TimeStrToDate'>: <function timestrtotime_sql>, <class 'sqlglot.expressions.temporal.TimeStrToTime'>: <function timestrtotime_sql>, <class 'sqlglot.expressions.temporal.TimeStrToUnix'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TimeToStr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TimeToUnix'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.ToChar'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.functions.TryCast'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.TsOrDiToDi'>: <function PrestoGenerator.<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.TsOrDsToDate'>: <function _ts_or_ds_to_date_sql>, <class 'sqlglot.expressions.string.Unhex'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.UnixToStr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.UnixToTime'>: <function _unix_to_time_sql>, <class 'sqlglot.expressions.temporal.UnixToTimeStr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.VariancePop'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.With'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.core.WithinGroup'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.math.Trunc'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.Xor'>: <function bool_xor_sql>, <class 'sqlglot.expressions.string.MD5Digest'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA1Digest'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA2'>: <function sha256_sql>, <class 'sqlglot.expressions.string.SHA2Digest'>: <function sha2_digest_sql>}
RESERVED_KEYWORDS =
{'deallocate', 'on', 'insert', 'true', 'exists', 'select', 'inner', 'natural', 'cross', 'in', 'join', 'escape', 'else', 'as', 'create', 'drop', 'delete', 'not', 'current_timestamp', 'cast', 'with', 'table', 'is', 'right', 'constraint', 'current_time', 'into', 'where', 'order', 'full', 'for', 'alter', 'having', 'left', 'intersect', 'execute', 'and', 'case', 'union', 'extract', 'by', 'between', 'group', 'describe', 'using', 'when', 'distinct', 'values', 'like', 'prepare', 'null', 'false', 'from', 'except', 'then', 'outer', 'or', 'end'}
456 def extract_sql(self, expression: exp.Extract) -> str: 457 date_part = expression.name 458 459 if not date_part.startswith("EPOCH"): 460 return super().extract_sql(expression) 461 462 if date_part == "EPOCH_MILLISECOND": 463 scale = 10**3 464 elif date_part == "EPOCH_MICROSECOND": 465 scale = 10**6 466 elif date_part == "EPOCH_NANOSECOND": 467 scale = 10**9 468 else: 469 scale = None 470 471 value = expression.expression 472 473 ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr()) 474 to_unix: exp.Expr = exp.TimeToUnix(this=ts) 475 476 if scale: 477 to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale)) 478 479 return self.sql(to_unix)
481 def jsonformat_sql(self, expression: exp.JSONFormat) -> str: 482 this = expression.this 483 is_json = expression.args.get("is_json") 484 485 if this and not (is_json or this.type): 486 from sqlglot.optimizer.annotate_types import annotate_types 487 488 this = annotate_types(this, dialect=self.dialect) 489 490 if not (is_json or this.is_type(exp.DType.JSON)): 491 this.replace(exp.cast(this, exp.DType.JSON)) 492 493 return self.function_fallback_sql(expression)
495 def md5_sql(self, expression: exp.MD5) -> str: 496 this = expression.this 497 498 if not this.type: 499 from sqlglot.optimizer.annotate_types import annotate_types 500 501 this = annotate_types(this, dialect=self.dialect) 502 503 if this.is_type(*exp.DataType.TEXT_TYPES): 504 this = exp.Encode(this=this, charset=exp.Literal.string("utf-8")) 505 506 return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this))))
508 def strtounix_sql(self, expression: exp.StrToUnix) -> str: 509 # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one. 510 # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a 511 # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback, 512 # which seems to be using the same time mapping as Hive, as per: 513 # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html 514 this = expression.this 515 value_as_text = exp.cast(this, exp.DType.TEXT) 516 value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this 517 518 parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression)) 519 520 formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression)) 521 parse_with_tz = self.func( 522 "PARSE_DATETIME", 523 formatted_value, 524 self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE), 525 ) 526 coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz) 527 return self.func("TO_UNIXTIME", coalesced)
534 def struct_sql(self, expression: exp.Struct) -> str: 535 if not expression.type: 536 from sqlglot.optimizer.annotate_types import annotate_types 537 538 annotate_types(expression, dialect=self.dialect) 539 540 values: list[str] = [] 541 schema: list[str] = [] 542 unknown_type = False 543 544 for e in expression.expressions: 545 if isinstance(e, exp.PropertyEQ): 546 if e.type and e.type.is_type(exp.DType.UNKNOWN): 547 unknown_type = True 548 else: 549 schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}") 550 values.append(self.sql(e, "expression")) 551 else: 552 values.append(self.sql(e)) 553 554 size = len(expression.expressions) 555 556 if not size or len(schema) != size: 557 if unknown_type: 558 self.unsupported( 559 "Cannot convert untyped key-value definitions (try annotate_types)." 560 ) 561 return self.func("ROW", *values) 562 return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))"
def
offset_limit_modifiers( self, expression: sqlglot.expressions.core.Expr, fetch: bool, limit: sqlglot.expressions.query.Fetch | sqlglot.expressions.query.Limit | None) -> list[str]:
582 def create_sql(self, expression: exp.Create) -> str: 583 """ 584 Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression), 585 so we need to remove them 586 """ 587 kind = expression.args["kind"] 588 schema = expression.this 589 if kind == "VIEW" and schema.expressions: 590 expression.this.set("expressions", None) 591 return super().create_sql(expression)
Presto doesn't support CREATE VIEW with expressions (ex: CREATE VIEW x (cola) then (cola) is the expression),
so we need to remove them
593 def delete_sql(self, expression: exp.Delete) -> str: 594 """ 595 Presto only supports DELETE FROM for a single table without an alias, so we need 596 to remove the unnecessary parts. If the original DELETE statement contains more 597 than one table to be deleted, we can't safely map it 1-1 to a Presto statement. 598 """ 599 tables = expression.args.get("tables") or [expression.this] 600 if len(tables) > 1: 601 return super().delete_sql(expression) 602 603 table = tables[0] 604 expression.set("this", table) 605 expression.set("tables", None) 606 607 if isinstance(table, exp.Table): 608 table_alias = table.args.get("alias") 609 if table_alias: 610 table_alias.pop() 611 expression = t.cast(exp.Delete, expression.transform(unqualify_columns)) 612 613 return super().delete_sql(expression)
Presto only supports DELETE FROM for a single table without an alias, so we need to remove the unnecessary parts. If the original DELETE statement contains more than one table to be deleted, we can't safely map it 1-1 to a Presto statement.
615 def jsonextract_sql(self, expression: exp.JSONExtract) -> str: 616 is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True) 617 618 # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks 619 # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino 620 if not expression.args.get("variant_extract") or is_json_extract: 621 return self.func( 622 "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions 623 ) 624 625 this = self.sql(expression, "this") 626 627 # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y 628 segments = [] 629 for path_key in expression.expression.expressions[1:]: 630 if not isinstance(path_key, exp.JSONPathKey): 631 # Cannot transpile subscripts, wildcards etc to dot notation 632 self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access") 633 continue 634 key = path_key.this 635 if not exp.SAFE_IDENTIFIER_RE.match(key): 636 key = f'"{key}"' 637 segments.append(f".{key}") 638 639 expr = "".join(segments) 640 641 return f"{this}{expr}"
Inherited Members
- sqlglot.generator.Generator
- Generator
- NULL_ORDERING_SUPPORTED
- WINDOW_FUNCS_WITH_NULL_ORDERING
- IGNORE_NULLS_IN_FUNC
- IGNORE_NULLS_BEFORE_ORDER
- LOCKING_READS_SUPPORTED
- WRAP_DERIVED_VALUES
- CREATE_FUNCTION_RETURN_AS
- MATCHED_BY_SOURCE
- SUPPORTS_MERGE_WHERE
- SINGLE_STRING_INTERVAL
- LIMIT_FETCH
- RENAME_TABLE_WITH_DB
- GROUPINGS_SEP
- INDEX_ON
- INOUT_SEPARATOR
- DIRECTED_JOINS
- QUERY_HINT_SEP
- DUPLICATE_KEY_UPDATE_WITH_SET
- LIMIT_IS_TOP
- RETURNING_END
- EXTRACT_ALLOWS_QUOTES
- VALUES_AS_TABLE
- ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
- UNNEST_WITH_ORDINALITY
- AGGREGATE_FILTER_SUPPORTED
- SEMI_ANTI_JOIN_WITH_SIDE
- COMPUTED_COLUMN_WITH_TYPE
- SUPPORTS_TABLE_COPY
- TABLESAMPLE_REQUIRES_PARENS
- TABLESAMPLE_SIZE_IS_ROWS
- TABLESAMPLE_KEYWORDS
- TABLESAMPLE_WITH_METHOD
- TABLESAMPLE_SEED_KEYWORD
- COLLATE_IS_FUNC
- DATA_TYPE_SPECIFIERS_ALLOWED
- ENSURE_BOOLS
- CTE_RECURSIVE_KEYWORD_REQUIRED
- LAST_DAY_SUPPORTS_DATE_PART
- SUPPORTS_TABLE_ALIAS_COLUMNS
- UNPIVOT_ALIASES_ARE_IDENTIFIERS
- JSON_KEY_VALUE_PAIR_SEP
- INSERT_OVERWRITE
- SUPPORTS_SELECT_INTO
- SUPPORTS_UNLOGGED_TABLES
- SUPPORTS_CREATE_TABLE_LIKE
- JSON_TYPE_REQUIRED_FOR_EXTRACTION
- JSON_PATH_BRACKETED_KEY_SUPPORTED
- JSON_PATH_SINGLE_QUOTE_ESCAPE
- SUPPORTED_JSON_PATH_PARTS
- CAN_IMPLEMENT_ARRAY_ANY
- SUPPORTS_WINDOW_EXCLUDE
- SET_OP_MODIFIERS
- COPY_PARAMS_ARE_WRAPPED
- COPY_PARAMS_EQ_REQUIRED
- COPY_HAS_INTO_KEYWORD
- TRY_SUPPORTED
- SUPPORTS_UESCAPE
- UNICODE_SUBSTITUTE
- STAR_EXCEPT
- WITH_PROPERTIES_PREFIX
- QUOTE_JSON_PATH
- SUPPORTS_EXPLODING_PROJECTIONS
- ARRAY_CONCAT_IS_VAR_LEN
- SUPPORTS_CONVERT_TIMEZONE
- SUPPORTS_UNIX_SECONDS
- ALTER_SET_WRAPPED
- NORMALIZE_EXTRACT_DATE_PARTS
- ALTER_SET_TYPE
- ARRAY_SIZE_DIM_REQUIRED
- SUPPORTS_BETWEEN_FLAGS
- SUPPORTS_LIKE_QUANTIFIERS
- MATCH_AGAINST_TABLE_PREFIX
- SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD
- DECLARE_DEFAULT_ASSIGNMENT
- UPDATE_STATEMENT_SUPPORTS_FROM
- STAR_EXCLUDE_REQUIRES_DERIVED_TABLE
- SUPPORTS_DROP_ALTER_ICEBERG_PROPERTY
- UNSUPPORTED_TYPES
- TIME_PART_SINGULARS
- TOKEN_MAPPING
- PARAMETER_TOKEN
- NAMED_PLACEHOLDER_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
- SAFE_JSON_PATH_KEY_RE
- 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_parts
- 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
- 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_sql
- directory_sql
- drop_sql
- set_operation
- set_operations
- fetch_sql
- limitoptions_sql
- filter_sql
- hint_sql
- indexparameters_sql
- index_sql
- identifier_sql
- hex_sql
- lowerhex_sql
- inputoutputformat_sql
- national_sql
- partition_sql
- properties_sql
- root_properties
- properties
- with_properties
- locate_properties
- property_name
- property_sql
- uuidproperty_sql
- likeproperty_sql
- fallbackproperty_sql
- journalproperty_sql
- freespaceproperty_sql
- checksumproperty_sql
- mergeblockratioproperty_sql
- moduleproperty_sql
- datablocksizeproperty_sql
- blockcompressionproperty_sql
- isolatedloadingproperty_sql
- partitionboundspec_sql
- partitionedofproperty_sql
- lockingproperty_sql
- withdataproperty_sql
- withsystemversioningproperty_sql
- insert_sql
- introducer_sql
- kill_sql
- pseudotype_sql
- objectidentifier_sql
- onconflict_sql
- returning_sql
- rowformatdelimitedproperty_sql
- withtablehint_sql
- indextablehint_sql
- historicaldata_sql
- table_parts
- table_sql
- tablefromrows_sql
- tablesample_sql
- pivot_sql
- version_sql
- tuple_sql
- update_sql
- values_sql
- var_sql
- into_sql
- from_sql
- groupingsets_sql
- rollup_sql
- rollupindex_sql
- rollupproperty_sql
- cube_sql
- group_sql
- having_sql
- connect_sql
- prior_sql
- join_sql
- lambda_sql
- lateral_op
- lateral_sql
- limit_sql
- offset_sql
- setitem_sql
- set_sql
- queryband_sql
- pragma_sql
- lock_sql
- literal_sql
- escape_str
- loaddata_sql
- null_sql
- boolean_sql
- booland_sql
- boolor_sql
- order_sql
- withfill_sql
- cluster_sql
- distribute_sql
- sort_sql
- ordered_sql
- matchrecognizemeasure_sql
- matchrecognize_sql
- query_modifiers
- options_modifier
- for_modifiers
- queryoption_sql
- after_limit_modifiers
- select_sql
- schema_sql
- schema_columns_sql
- star_sql
- parameter_sql
- sessionparameter_sql
- placeholder_sql
- subquery_sql
- qualify_sql
- unnest_sql
- prewhere_sql
- where_sql
- window_sql
- partition_by_sql
- windowspec_sql
- withingroup_sql
- between_sql
- bracket_offset_expressions
- all_sql
- any_sql
- exists_sql
- case_sql
- constraint_sql
- nextvaluefor_sql
- trim_sql
- convert_concat_args
- concat_sql
- concatws_sql
- check_sql
- foreignkey_sql
- primarykey_sql
- if_sql
- matchagainst_sql
- jsonkeyvalue_sql
- jsonpath_sql
- json_path_part
- formatjson_sql
- formatphrase_sql
- jsonarray_sql
- jsonarrayagg_sql
- jsoncolumndef_sql
- jsonschema_sql
- jsontable_sql
- openjsoncolumndef_sql
- openjson_sql
- in_sql
- in_unnest_op
- return_sql
- reference_sql
- anonymous_sql
- paren_sql
- neg_sql
- not_sql
- alias_sql
- pivotalias_sql
- aliases_sql
- atindex_sql
- attimezone_sql
- fromtimezone_sql
- add_sql
- and_sql
- or_sql
- xor_sql
- connector_sql
- bitwiseand_sql
- bitwiseleftshift_sql
- bitwisenot_sql
- bitwiseor_sql
- bitwiserightshift_sql
- bitwisexor_sql
- cast_sql
- strtotime_sql
- currentdate_sql
- collate_sql
- command_sql
- comment_sql
- mergetreettlaction_sql
- mergetreettl_sql
- commit_sql
- rollback_sql
- altercolumn_sql
- alterindex_sql
- alterdiststyle_sql
- altersortkey_sql
- alterrename_sql
- renamecolumn_sql
- alterset_sql
- alter_sql
- altersession_sql
- add_column_sql
- droppartition_sql
- addconstraint_sql
- addpartition_sql
- distinct_sql
- ignorenulls_sql
- respectnulls_sql
- havingmax_sql
- intdiv_sql
- dpipe_sql
- div_sql
- safedivide_sql
- overlaps_sql
- distance_sql
- dot_sql
- eq_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
- mod_sql
- mul_sql
- neq_sql
- nullsafeeq_sql
- nullsafeneq_sql
- sub_sql
- trycast_sql
- jsoncast_sql
- try_sql
- log_sql
- use_sql
- binary
- ceil_floor
- function_fallback_sql
- func
- format_args
- too_wide
- format_time
- expressions
- op_expressions
- naked_property
- tag_sql
- token_sql
- userdefinedfunction_sql
- joinhint_sql
- kwarg_sql
- when_sql
- whens_sql
- merge_sql
- tochar_sql
- tonumber_sql
- dictproperty_sql
- dictrange_sql
- dictsubproperty_sql
- duplicatekeyproperty_sql
- uniquekeyproperty_sql
- distributedbyproperty_sql
- oncluster_sql
- clusteredbyproperty_sql
- anyvalue_sql
- querytransform_sql
- indexconstraintoption_sql
- checkcolumnconstraint_sql
- indexcolumnconstraint_sql
- nvl2_sql
- comprehension_sql
- columnprefix_sql
- opclass_sql
- predict_sql
- generateembedding_sql
- generatetext_sql
- generatetable_sql
- generatebool_sql
- generateint_sql
- generatedouble_sql
- mltranslate_sql
- mlforecast_sql
- aiforecast_sql
- featuresattime_sql
- vectorsearch_sql
- forin_sql
- refresh_sql
- toarray_sql
- tsordstotime_sql
- tsordstotimestamp_sql
- tsordstodatetime_sql
- tsordstodate_sql
- unixdate_sql
- lastday_sql
- dateadd_sql
- arrayany_sql
- partitionrange_sql
- truncatetable_sql
- convert_sql
- copyparameter_sql
- credentials_sql
- copy_sql
- semicolon_sql
- datadeletionproperty_sql
- maskingpolicycolumnconstraint_sql
- gapfill_sql
- scope_resolution
- scoperesolution_sql
- parsejson_sql
- rand_sql
- changes_sql
- pad_sql
- summarize_sql
- explodinggenerateseries_sql
- converttimezone_sql
- json_sql
- jsonvalue_sql
- skipjsoncolumn_sql
- conditionalinsert_sql
- multitableinserts_sql
- oncondition_sql
- jsonextractquote_sql
- jsonexists_sql
- arrayagg_sql
- slice_sql
- apply_sql
- grant_sql
- revoke_sql
- grantprivilege_sql
- grantprincipal_sql
- columns_sql
- overlay_sql
- todouble_sql
- string_sql
- median_sql
- overflowtruncatebehavior_sql
- unixseconds_sql
- arraysize_sql
- attach_sql
- detach_sql
- attachoption_sql
- watermarkcolumnconstraint_sql
- encodeproperty_sql
- includeproperty_sql
- xmlelement_sql
- xmlkeyvalueoption_sql
- partitionbyrangeproperty_sql
- partitionbyrangepropertydynamic_sql
- unpivotcolumns_sql
- analyzesample_sql
- analyzestatistics_sql
- analyzehistogram_sql
- analyzedelete_sql
- analyzelistchainedrows_sql
- analyzevalidate_sql
- analyze_sql
- xmltable_sql
- xmlnamespace_sql
- export_sql
- declare_sql
- declareitem_sql
- recursivewithsearch_sql
- parameterizedagg_sql
- anonymousaggfunc_sql
- combinedaggfunc_sql
- combinedparameterizedagg_sql
- show_sql
- install_sql
- get_put_sql
- translatecharacters_sql
- decodecase_sql
- semanticview_sql
- getextract_sql
- datefromunixdate_sql
- space_sql
- buildproperty_sql
- refreshtriggerproperty_sql
- modelattribute_sql
- directorystage_sql
- uuid_sql
- initcap_sql
- localtime_sql
- localtimestamp_sql
- weekstart_sql
- chr_sql
- block_sql
- storedprocedure_sql
- ifblock_sql
- whileblock_sql
- execute_sql
- executesql_sql
- altermodifysqlsecurity_sql
- usingproperty_sql
- renameindex_sql