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