sqlglot.generators.presto
1from __future__ import annotations 2 3import typing as t 4 5from sqlglot import exp, generator, transforms 6from sqlglot.dialects.dialect import ( 7 bool_xor_sql, 8 bracket_to_element_at_sql, 9 datestrtodate_sql, 10 encode_decode_sql, 11 if_sql, 12 left_to_substring_sql, 13 no_ilike_sql, 14 no_pivot_sql, 15 no_timestamp_sql, 16 regexp_extract_sql, 17 rename_func, 18 right_to_substring_sql, 19 sha256_sql, 20 strposition_sql, 21 struct_extract_sql, 22 timestamptrunc_sql, 23 timestrtotime_sql, 24 ts_or_ds_add_cast, 25 unit_to_str, 26 sequence_sql, 27 explode_to_unnest_sql, 28 sha2_digest_sql, 29) 30from sqlglot.dialects.hive import Hive 31from sqlglot.generator import unsupported_args 32from sqlglot.optimizer.scope import find_all_in_scope 33from sqlglot.transforms import unqualify_columns 34 35DATE_ADD_OR_SUB = t.Union[exp.DateAdd, exp.TimestampAdd, exp.DateSub] 36 37 38def _initcap_sql(self: PrestoGenerator, expression: exp.Initcap) -> str: 39 delimiters = expression.expression 40 if delimiters and not ( 41 delimiters.is_string and delimiters.this == self.dialect.INITCAP_DEFAULT_DELIMITER_CHARS 42 ): 43 self.unsupported("INITCAP does not support custom delimiters") 44 45 regex = r"(\w)(\w*)" 46 return f"REGEXP_REPLACE({self.sql(expression, 'this')}, '{regex}', x -> UPPER(x[1]) || LOWER(x[2]))" 47 48 49def _no_sort_array(self: PrestoGenerator, expression: exp.SortArray) -> str: 50 if expression.args.get("asc") == exp.false(): 51 comparator = "(a, b) -> CASE WHEN a < b THEN 1 WHEN a > b THEN -1 ELSE 0 END" 52 else: 53 comparator = None 54 return self.func("ARRAY_SORT", expression.this, comparator) 55 56 57def _schema_sql(self: PrestoGenerator, expression: exp.Schema) -> str: 58 if isinstance(expression.parent, exp.PartitionedByProperty): 59 # Any columns in the ARRAY[] string literals should not be quoted 60 expression.transform(lambda n: n.name if isinstance(n, exp.Identifier) else n, copy=False) 61 62 partition_exprs = [ 63 self.sql(c) if isinstance(c, (exp.Func, exp.Property)) else self.sql(c, "this") 64 for c in expression.expressions 65 ] 66 return self.sql(exp.Array(expressions=[exp.Literal.string(c) for c in partition_exprs])) 67 68 if expression.parent: 69 for schema in expression.parent.find_all(exp.Schema): 70 if schema is expression: 71 continue 72 73 column_defs = schema.find_all(exp.ColumnDef) 74 if column_defs and isinstance(schema.parent, exp.Property): 75 expression.expressions.extend(column_defs) 76 77 return self.schema_sql(expression) 78 79 80def _quantile_sql(self: PrestoGenerator, expression: exp.Quantile) -> str: 81 self.unsupported("Presto does not support exact quantiles") 82 return self.func("APPROX_PERCENTILE", expression.this, expression.args.get("quantile")) 83 84 85def _str_to_time_sql( 86 self: PrestoGenerator, expression: exp.StrToDate | exp.StrToTime | exp.TsOrDsToDate 87) -> str: 88 return self.func("DATE_PARSE", expression.this, self.format_time(expression)) 89 90 91def _ts_or_ds_to_date_sql(self: PrestoGenerator, expression: exp.TsOrDsToDate) -> str: 92 time_format = self.format_time(expression) 93 dialect_class = type(self.dialect) 94 if time_format and time_format not in (dialect_class.TIME_FORMAT, dialect_class.DATE_FORMAT): 95 return self.sql(exp.cast(_str_to_time_sql(self, expression), exp.DType.DATE)) 96 return self.sql(exp.cast(exp.cast(expression.this, exp.DType.TIMESTAMP), exp.DType.DATE)) 97 98 99def _ts_or_ds_add_sql(self: PrestoGenerator, expression: exp.TsOrDsAdd) -> str: 100 expression = ts_or_ds_add_cast(expression) 101 unit = unit_to_str(expression) 102 return self.func("DATE_ADD", unit, expression.expression, expression.this) 103 104 105def _ts_or_ds_diff_sql(self: PrestoGenerator, expression: exp.TsOrDsDiff) -> str: 106 this = exp.cast(expression.this, exp.DType.TIMESTAMP) 107 expr = exp.cast(expression.expression, exp.DType.TIMESTAMP) 108 unit = unit_to_str(expression) 109 return self.func("DATE_DIFF", unit, expr, this) 110 111 112def _first_last_sql(self: PrestoGenerator, expression: exp.Func) -> str: 113 """ 114 Trino doesn't support FIRST / LAST as functions, but they're valid in the context 115 of MATCH_RECOGNIZE, so we need to preserve them in that case. In all other cases 116 they're converted into an ARBITRARY call. 117 118 Reference: https://trino.io/docs/current/sql/match-recognize.html#logical-navigation-functions 119 """ 120 if isinstance(expression.find_ancestor(exp.MatchRecognize, exp.Select), exp.MatchRecognize): 121 return self.function_fallback_sql(expression) 122 123 return rename_func("ARBITRARY")(self, expression) 124 125 126def _unix_to_time_sql(self: PrestoGenerator, expression: exp.UnixToTime) -> str: 127 scale = expression.args.get("scale") 128 timestamp = self.sql(expression, "this") 129 if scale in (None, exp.UnixToTime.SECONDS): 130 return rename_func("FROM_UNIXTIME")(self, expression) 131 132 return f"FROM_UNIXTIME(CAST({timestamp} AS DOUBLE) / POW(10, {scale}))" 133 134 135def _to_int(self: PrestoGenerator, expression: exp.Expr) -> exp.Expr: 136 if not expression.type: 137 from sqlglot.optimizer.annotate_types import annotate_types 138 139 annotate_types(expression, dialect=self.dialect) 140 if expression.type and expression.type.this not in exp.DataType.INTEGER_TYPES: 141 return exp.cast(expression, to=exp.DType.BIGINT) 142 return expression 143 144 145def _date_delta_sql( 146 name: str, negate_interval: bool = False 147) -> t.Callable[[PrestoGenerator, DATE_ADD_OR_SUB], str]: 148 def _delta_sql(self: PrestoGenerator, expression: DATE_ADD_OR_SUB) -> str: 149 interval = _to_int(self, expression.expression) 150 return self.func( 151 name, 152 unit_to_str(expression), 153 interval * (-1) if negate_interval else interval, 154 expression.this, 155 ) 156 157 return _delta_sql 158 159 160def _explode_to_unnest_sql(self: PrestoGenerator, expression: exp.Lateral) -> str: 161 explode = expression.this 162 if isinstance(explode, exp.Explode): 163 exploded_type = explode.this.type 164 alias = expression.args.get("alias") 165 166 # This attempts a best-effort transpilation of LATERAL VIEW EXPLODE on a struct array 167 if ( 168 isinstance(alias, exp.TableAlias) 169 and isinstance(exploded_type, exp.DataType) 170 and exploded_type.is_type(exp.DType.ARRAY) 171 and exploded_type.expressions 172 and exploded_type.expressions[0].is_type(exp.DType.STRUCT) 173 ): 174 # When unnesting a ROW in Presto, it produces N columns, so we need to fix the alias 175 alias.set("columns", [c.this.copy() for c in exploded_type.expressions[0].expressions]) 176 elif isinstance(explode, exp.Inline): 177 explode.replace(exp.Explode(this=explode.this.copy())) 178 179 return explode_to_unnest_sql(self, expression) 180 181 182def amend_exploded_column_table(expression: exp.Expr) -> exp.Expr: 183 # We check for expression.type because the columns can be amended only if types were inferred 184 if isinstance(expression, exp.Select) and expression.type: 185 for lateral in expression.args.get("laterals") or []: 186 alias = lateral.args.get("alias") 187 if ( 188 not isinstance(lateral.this, exp.Explode) 189 or not isinstance(alias, exp.TableAlias) 190 or len(alias.columns) != 1 191 ): 192 continue 193 194 new_table = alias.this 195 old_table = alias.columns[0].name.lower() 196 197 # When transpiling a LATERAL VIEW EXPLODE Spark query, the exploded fields may be qualified 198 # with the struct column, resulting in invalid Presto references that need to be amended 199 for column in find_all_in_scope(expression, exp.Column): 200 if column.db.lower() == old_table: 201 column.set("table", column.args["db"].pop()) 202 elif column.table.lower() == old_table: 203 column.set("table", new_table.copy()) 204 elif column.name.lower() == old_table and isinstance(column.parent, exp.Dot): 205 column.parent.replace(exp.column(column.parent.expression, table=new_table)) 206 207 return expression 208 209 210class PrestoGenerator(generator.Generator): 211 SELECT_KINDS: tuple[str, ...] = () 212 SUPPORTS_DECODE_CASE = False 213 214 AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS 215 216 INTERVAL_ALLOWS_PLURAL_FORM = False 217 JOIN_HINTS = False 218 TABLE_HINTS = False 219 QUERY_HINTS = False 220 IS_BOOL_ALLOWED = False 221 TZ_TO_WITH_TIME_ZONE = True 222 NVL2_SUPPORTED = False 223 STRUCT_DELIMITER = ("(", ")") 224 LIMIT_ONLY_LITERALS = True 225 SUPPORTS_SINGLE_ARG_CONCAT = False 226 LIKE_PROPERTY_INSIDE_SCHEMA = True 227 MULTI_ARG_DISTINCT = False 228 SUPPORTS_TO_NUMBER = False 229 HEX_FUNC = "TO_HEX" 230 PARSE_JSON_NAME: str | None = "JSON_PARSE" 231 PAD_FILL_PATTERN_IS_REQUIRED = True 232 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 233 SUPPORTS_MEDIAN = False 234 ARRAY_SIZE_NAME = "CARDINALITY" 235 236 PROPERTIES_LOCATION = { 237 **generator.Generator.PROPERTIES_LOCATION, 238 exp.LocationProperty: exp.Properties.Location.UNSUPPORTED, 239 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 240 } 241 242 TYPE_MAPPING = { 243 **generator.Generator.TYPE_MAPPING, 244 exp.DType.BINARY: "VARBINARY", 245 exp.DType.BIT: "BOOLEAN", 246 exp.DType.DATETIME: "TIMESTAMP", 247 exp.DType.DATETIME64: "TIMESTAMP", 248 exp.DType.FLOAT: "REAL", 249 exp.DType.HLLSKETCH: "HYPERLOGLOG", 250 exp.DType.INT: "INTEGER", 251 exp.DType.STRUCT: "ROW", 252 exp.DType.TEXT: "VARCHAR", 253 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 254 exp.DType.TIMESTAMPNTZ: "TIMESTAMP", 255 exp.DType.TIMETZ: "TIME", 256 } 257 258 TRANSFORMS = { 259 **generator.Generator.TRANSFORMS, 260 exp.AnyValue: rename_func("ARBITRARY"), 261 exp.ApproxQuantile: lambda self, e: self.func( 262 "APPROX_PERCENTILE", 263 e.this, 264 e.args.get("weight"), 265 e.args.get("quantile"), 266 e.args.get("accuracy"), 267 ), 268 exp.ArgMax: rename_func("MAX_BY"), 269 exp.ArgMin: rename_func("MIN_BY"), 270 exp.Array: transforms.preprocess( 271 [transforms.inherit_struct_field_names], 272 generator=lambda self, e: f"ARRAY[{self.expressions(e, flat=True)}]", 273 ), 274 exp.ArrayAny: rename_func("ANY_MATCH"), 275 exp.ArrayConcat: rename_func("CONCAT"), 276 exp.ArrayContains: rename_func("CONTAINS"), 277 exp.ArrayToString: rename_func("ARRAY_JOIN"), 278 exp.ArrayUniqueAgg: rename_func("SET_AGG"), 279 exp.ArraySlice: rename_func("SLICE"), 280 exp.AtTimeZone: rename_func("AT_TIMEZONE"), 281 exp.BitwiseAnd: lambda self, e: self.func("BITWISE_AND", e.this, e.expression), 282 exp.BitwiseLeftShift: rename_func("BITWISE_LEFT_SHIFT"), 283 exp.BitwiseNot: lambda self, e: self.func("BITWISE_NOT", e.this), 284 exp.BitwiseOr: lambda self, e: self.func("BITWISE_OR", e.this, e.expression), 285 exp.BitwiseRightShift: rename_func("BITWISE_RIGHT_SHIFT"), 286 exp.BitwiseXor: lambda self, e: self.func("BITWISE_XOR", e.this, e.expression), 287 exp.Cast: transforms.preprocess([transforms.epoch_cast_to_ts]), 288 exp.CurrentTime: lambda *_: "CURRENT_TIME", 289 exp.CurrentTimestamp: lambda *_: "CURRENT_TIMESTAMP", 290 exp.CurrentUser: lambda *_: "CURRENT_USER", 291 exp.DateAdd: _date_delta_sql("DATE_ADD"), 292 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", unit_to_str(e), e.expression, e.this), 293 exp.DateStrToDate: datestrtodate_sql, 294 exp.DateToDi: lambda self, e: ( 295 f"CAST(DATE_FORMAT({self.sql(e, 'this')}, {type(self.dialect).DATEINT_FORMAT}) AS INT)" 296 ), 297 exp.DateSub: _date_delta_sql("DATE_ADD", negate_interval=True), 298 exp.DayOfWeek: lambda self, e: f"(({self.func('DAY_OF_WEEK', e.this)} % 7) + 1)", 299 exp.DayOfWeekIso: rename_func("DAY_OF_WEEK"), 300 exp.Decode: lambda self, e: encode_decode_sql(self, e, "FROM_UTF8"), 301 exp.DiToDate: lambda self, e: ( 302 f"CAST(DATE_PARSE(CAST({self.sql(e, 'this')} AS VARCHAR), {type(self.dialect).DATEINT_FORMAT}) AS DATE)" 303 ), 304 exp.Encode: lambda self, e: encode_decode_sql(self, e, "TO_UTF8"), 305 exp.FileFormatProperty: lambda self, e: f"format={self.sql(exp.Literal.string(e.name))}", 306 exp.First: _first_last_sql, 307 exp.FromISO8601Date: rename_func("FROM_ISO8601_DATE"), 308 exp.FromISO8601Timestamp: rename_func("FROM_ISO8601_TIMESTAMP"), 309 exp.FromTimeZone: lambda self, e: ( 310 f"WITH_TIMEZONE({self.sql(e, 'this')}, {self.sql(e, 'zone')}) AT TIME ZONE 'UTC'" 311 ), 312 exp.GenerateSeries: sequence_sql, 313 exp.GenerateDateArray: sequence_sql, 314 exp.If: if_sql(), 315 exp.ILike: no_ilike_sql, 316 exp.Initcap: _initcap_sql, 317 exp.Last: _first_last_sql, 318 exp.LastDay: lambda self, e: self.func("LAST_DAY_OF_MONTH", e.this), 319 exp.Lateral: _explode_to_unnest_sql, 320 exp.Left: left_to_substring_sql, 321 exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")( 322 rename_func("LEVENSHTEIN_DISTANCE") 323 ), 324 exp.LogicalAnd: rename_func("BOOL_AND"), 325 exp.LogicalOr: rename_func("BOOL_OR"), 326 exp.Pivot: no_pivot_sql, 327 exp.Quantile: _quantile_sql, 328 exp.RegexpExtract: regexp_extract_sql, 329 exp.RegexpExtractAll: regexp_extract_sql, 330 exp.Right: right_to_substring_sql, 331 exp.Schema: _schema_sql, 332 exp.SchemaCommentProperty: lambda self, e: self.naked_property(e), 333 exp.Select: transforms.preprocess( 334 [ 335 transforms.eliminate_window_clause, 336 transforms.eliminate_qualify, 337 transforms.eliminate_distinct_on, 338 transforms.explode_projection_to_unnest(1), 339 transforms.eliminate_semi_and_anti_joins, 340 amend_exploded_column_table, 341 ] 342 ), 343 exp.SortArray: _no_sort_array, 344 exp.SqlSecurityProperty: lambda self, e: f"SECURITY {self.sql(e.this)}", 345 exp.StrPosition: lambda self, e: strposition_sql(self, e, supports_occurrence=True), 346 exp.StrToDate: lambda self, e: f"CAST({_str_to_time_sql(self, e)} AS DATE)", 347 exp.StrToMap: rename_func("SPLIT_TO_MAP"), 348 exp.StrToTime: _str_to_time_sql, 349 exp.StructExtract: struct_extract_sql, 350 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 351 exp.Timestamp: no_timestamp_sql, 352 exp.TimestampAdd: _date_delta_sql("DATE_ADD"), 353 exp.TimestampTrunc: timestamptrunc_sql(), 354 exp.TimeStrToDate: timestrtotime_sql, 355 exp.TimeStrToTime: timestrtotime_sql, 356 exp.TimeStrToUnix: lambda self, e: self.func( 357 "TO_UNIXTIME", self.func("DATE_PARSE", e.this, type(self.dialect).TIME_FORMAT) 358 ), 359 exp.TimeToStr: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 360 exp.TimeToUnix: rename_func("TO_UNIXTIME"), 361 exp.ToChar: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 362 exp.TryCast: transforms.preprocess([transforms.epoch_cast_to_ts]), 363 exp.TsOrDiToDi: lambda self, e: ( 364 f"CAST(SUBSTR(REPLACE(CAST({self.sql(e, 'this')} AS VARCHAR), '-', ''), 1, 8) AS INT)" 365 ), 366 exp.TsOrDsAdd: _ts_or_ds_add_sql, 367 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 368 exp.TsOrDsToDate: _ts_or_ds_to_date_sql, 369 exp.Unhex: rename_func("FROM_HEX"), 370 exp.UnixToStr: lambda self, e: ( 371 f"DATE_FORMAT(FROM_UNIXTIME({self.sql(e, 'this')}), {self.format_time(e)})" 372 ), 373 exp.UnixToTime: _unix_to_time_sql, 374 exp.UnixToTimeStr: lambda self, e: f"CAST(FROM_UNIXTIME({self.sql(e, 'this')}) AS VARCHAR)", 375 exp.VariancePop: rename_func("VAR_POP"), 376 exp.With: transforms.preprocess([transforms.add_recursive_cte_column_names]), 377 exp.WithinGroup: transforms.preprocess([transforms.remove_within_group_for_percentiles]), 378 # Note: Presto's TRUNCATE always returns DOUBLE, even with decimals=0, whereas 379 # most dialects return INT (SQLite also returns REAL, see sqlite.py). This creates 380 # a bidirectional transpilation gap: Presto→Other may change float division to int 381 # division, and vice versa. Modeling precisely would require exp.FloatTrunc or 382 # similar, deemed overengineering for this subtle semantic difference. 383 exp.Trunc: rename_func("TRUNCATE"), 384 exp.Xor: bool_xor_sql, 385 exp.MD5Digest: rename_func("MD5"), 386 exp.SHA: rename_func("SHA1"), 387 exp.SHA1Digest: rename_func("SHA1"), 388 exp.SHA2: sha256_sql, 389 exp.SHA2Digest: sha2_digest_sql, 390 exp.Substring: rename_func("SUBSTR"), 391 } 392 393 RESERVED_KEYWORDS = { 394 "alter", 395 "and", 396 "as", 397 "between", 398 "by", 399 "case", 400 "cast", 401 "constraint", 402 "create", 403 "cross", 404 "current_time", 405 "current_timestamp", 406 "deallocate", 407 "delete", 408 "describe", 409 "distinct", 410 "drop", 411 "else", 412 "end", 413 "escape", 414 "except", 415 "execute", 416 "exists", 417 "extract", 418 "false", 419 "for", 420 "from", 421 "full", 422 "group", 423 "having", 424 "in", 425 "inner", 426 "insert", 427 "intersect", 428 "into", 429 "is", 430 "join", 431 "left", 432 "like", 433 "natural", 434 "not", 435 "null", 436 "on", 437 "or", 438 "order", 439 "outer", 440 "prepare", 441 "right", 442 "select", 443 "table", 444 "then", 445 "true", 446 "union", 447 "using", 448 "values", 449 "when", 450 "where", 451 "with", 452 } 453 454 def extract_sql(self, expression: exp.Extract) -> str: 455 date_part = expression.name 456 457 if not date_part.startswith("EPOCH"): 458 return super().extract_sql(expression) 459 460 if date_part == "EPOCH_MILLISECOND": 461 scale = 10**3 462 elif date_part == "EPOCH_MICROSECOND": 463 scale = 10**6 464 elif date_part == "EPOCH_NANOSECOND": 465 scale = 10**9 466 else: 467 scale = None 468 469 value = expression.expression 470 471 ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr()) 472 to_unix: exp.Expr = exp.TimeToUnix(this=ts) 473 474 if scale: 475 to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale)) 476 477 return self.sql(to_unix) 478 479 def jsonformat_sql(self, expression: exp.JSONFormat) -> str: 480 this = expression.this 481 is_json = expression.args.get("is_json") 482 483 if this and not (is_json or this.type): 484 from sqlglot.optimizer.annotate_types import annotate_types 485 486 this = annotate_types(this, dialect=self.dialect) 487 488 if not (is_json or this.is_type(exp.DType.JSON)): 489 this.replace(exp.cast(this, exp.DType.JSON)) 490 491 return self.function_fallback_sql(expression) 492 493 def md5_sql(self, expression: exp.MD5) -> str: 494 this = expression.this 495 496 if not this.type: 497 from sqlglot.optimizer.annotate_types import annotate_types 498 499 this = annotate_types(this, dialect=self.dialect) 500 501 if this.is_type(*exp.DataType.TEXT_TYPES): 502 this = exp.Encode(this=this, charset=exp.Literal.string("utf-8")) 503 504 return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this)))) 505 506 def strtounix_sql(self, expression: exp.StrToUnix) -> str: 507 # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one. 508 # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a 509 # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback, 510 # which seems to be using the same time mapping as Hive, as per: 511 # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html 512 this = expression.this 513 value_as_text = exp.cast(this, exp.DType.TEXT) 514 value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this 515 516 parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression)) 517 518 formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression)) 519 parse_with_tz = self.func( 520 "PARSE_DATETIME", 521 formatted_value, 522 self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE), 523 ) 524 coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz) 525 return self.func("TO_UNIXTIME", coalesced) 526 527 def bracket_sql(self, expression: exp.Bracket) -> str: 528 if expression.args.get("safe"): 529 return bracket_to_element_at_sql(self, expression) 530 return super().bracket_sql(expression) 531 532 def struct_sql(self, expression: exp.Struct) -> str: 533 if not expression.type: 534 from sqlglot.optimizer.annotate_types import annotate_types 535 536 annotate_types(expression, dialect=self.dialect) 537 538 values: list[str] = [] 539 schema: list[str] = [] 540 unknown_type = False 541 542 for e in expression.expressions: 543 if isinstance(e, exp.PropertyEQ): 544 if e.type and e.type.is_type(exp.DType.UNKNOWN): 545 unknown_type = True 546 else: 547 schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}") 548 values.append(self.sql(e, "expression")) 549 else: 550 values.append(self.sql(e)) 551 552 size = len(expression.expressions) 553 554 if not size or len(schema) != size: 555 if unknown_type: 556 self.unsupported( 557 "Cannot convert untyped key-value definitions (try annotate_types)." 558 ) 559 return self.func("ROW", *values) 560 return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))" 561 562 def interval_sql(self, expression: exp.Interval) -> str: 563 if expression.this and expression.text("unit").upper().startswith("WEEK"): 564 return f"({expression.this.name} * INTERVAL '7' DAY)" 565 return super().interval_sql(expression) 566 567 def transaction_sql(self, expression: exp.Transaction) -> str: 568 modes = expression.args.get("modes") 569 modes = f" {', '.join(modes)}" if modes else "" 570 return f"START TRANSACTION{modes}" 571 572 def offset_limit_modifiers( 573 self, expression: exp.Expr, fetch: bool, limit: exp.Fetch | exp.Limit | None 574 ) -> list[str]: 575 return [ 576 self.sql(expression, "offset"), 577 self.sql(limit), 578 ] 579 580 def create_sql(self, expression: exp.Create) -> str: 581 """ 582 Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression), 583 so we need to remove them 584 """ 585 kind = expression.args["kind"] 586 schema = expression.this 587 if kind == "VIEW" and schema.expressions: 588 expression.this.set("expressions", None) 589 return super().create_sql(expression) 590 591 def delete_sql(self, expression: exp.Delete) -> str: 592 """ 593 Presto only supports DELETE FROM for a single table without an alias, so we need 594 to remove the unnecessary parts. If the original DELETE statement contains more 595 than one table to be deleted, we can't safely map it 1-1 to a Presto statement. 596 """ 597 tables = expression.args.get("tables") or [expression.this] 598 if len(tables) > 1: 599 return super().delete_sql(expression) 600 601 table = tables[0] 602 expression.set("this", table) 603 expression.set("tables", None) 604 605 if isinstance(table, exp.Table): 606 table_alias = table.args.get("alias") 607 if table_alias: 608 table_alias.pop() 609 expression = t.cast(exp.Delete, expression.transform(unqualify_columns)) 610 611 return super().delete_sql(expression) 612 613 def jsonextract_sql(self, expression: exp.JSONExtract) -> str: 614 is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True) 615 616 # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks 617 # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino 618 if not expression.args.get("variant_extract") or is_json_extract: 619 return self.func( 620 "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions 621 ) 622 623 this = self.sql(expression, "this") 624 625 # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y 626 segments = [] 627 for path_key in expression.expression.expressions[1:]: 628 if not isinstance(path_key, exp.JSONPathKey): 629 # Cannot transpile subscripts, wildcards etc to dot notation 630 self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access") 631 continue 632 key = path_key.this 633 if not exp.SAFE_IDENTIFIER_RE.match(key): 634 key = f'"{key}"' 635 segments.append(f".{key}") 636 637 expr = "".join(segments) 638 639 return f"{this}{expr}" 640 641 def groupconcat_sql(self, expression: exp.GroupConcat) -> str: 642 return self.func( 643 "ARRAY_JOIN", 644 self.func("ARRAY_AGG", expression.this), 645 expression.args.get("separator"), 646 )
DATE_ADD_OR_SUB =
typing.Union[sqlglot.expressions.temporal.DateAdd, sqlglot.expressions.temporal.TimestampAdd, sqlglot.expressions.temporal.DateSub]
def
amend_exploded_column_table( expression: sqlglot.expressions.core.Expr) -> sqlglot.expressions.core.Expr:
183def amend_exploded_column_table(expression: exp.Expr) -> exp.Expr: 184 # We check for expression.type because the columns can be amended only if types were inferred 185 if isinstance(expression, exp.Select) and expression.type: 186 for lateral in expression.args.get("laterals") or []: 187 alias = lateral.args.get("alias") 188 if ( 189 not isinstance(lateral.this, exp.Explode) 190 or not isinstance(alias, exp.TableAlias) 191 or len(alias.columns) != 1 192 ): 193 continue 194 195 new_table = alias.this 196 old_table = alias.columns[0].name.lower() 197 198 # When transpiling a LATERAL VIEW EXPLODE Spark query, the exploded fields may be qualified 199 # with the struct column, resulting in invalid Presto references that need to be amended 200 for column in find_all_in_scope(expression, exp.Column): 201 if column.db.lower() == old_table: 202 column.set("table", column.args["db"].pop()) 203 elif column.table.lower() == old_table: 204 column.set("table", new_table.copy()) 205 elif column.name.lower() == old_table and isinstance(column.parent, exp.Dot): 206 column.parent.replace(exp.column(column.parent.expression, table=new_table)) 207 208 return expression
211class PrestoGenerator(generator.Generator): 212 SELECT_KINDS: tuple[str, ...] = () 213 SUPPORTS_DECODE_CASE = False 214 215 AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS 216 217 INTERVAL_ALLOWS_PLURAL_FORM = False 218 JOIN_HINTS = False 219 TABLE_HINTS = False 220 QUERY_HINTS = False 221 IS_BOOL_ALLOWED = False 222 TZ_TO_WITH_TIME_ZONE = True 223 NVL2_SUPPORTED = False 224 STRUCT_DELIMITER = ("(", ")") 225 LIMIT_ONLY_LITERALS = True 226 SUPPORTS_SINGLE_ARG_CONCAT = False 227 LIKE_PROPERTY_INSIDE_SCHEMA = True 228 MULTI_ARG_DISTINCT = False 229 SUPPORTS_TO_NUMBER = False 230 HEX_FUNC = "TO_HEX" 231 PARSE_JSON_NAME: str | None = "JSON_PARSE" 232 PAD_FILL_PATTERN_IS_REQUIRED = True 233 EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False 234 SUPPORTS_MEDIAN = False 235 ARRAY_SIZE_NAME = "CARDINALITY" 236 237 PROPERTIES_LOCATION = { 238 **generator.Generator.PROPERTIES_LOCATION, 239 exp.LocationProperty: exp.Properties.Location.UNSUPPORTED, 240 exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, 241 } 242 243 TYPE_MAPPING = { 244 **generator.Generator.TYPE_MAPPING, 245 exp.DType.BINARY: "VARBINARY", 246 exp.DType.BIT: "BOOLEAN", 247 exp.DType.DATETIME: "TIMESTAMP", 248 exp.DType.DATETIME64: "TIMESTAMP", 249 exp.DType.FLOAT: "REAL", 250 exp.DType.HLLSKETCH: "HYPERLOGLOG", 251 exp.DType.INT: "INTEGER", 252 exp.DType.STRUCT: "ROW", 253 exp.DType.TEXT: "VARCHAR", 254 exp.DType.TIMESTAMPTZ: "TIMESTAMP", 255 exp.DType.TIMESTAMPNTZ: "TIMESTAMP", 256 exp.DType.TIMETZ: "TIME", 257 } 258 259 TRANSFORMS = { 260 **generator.Generator.TRANSFORMS, 261 exp.AnyValue: rename_func("ARBITRARY"), 262 exp.ApproxQuantile: lambda self, e: self.func( 263 "APPROX_PERCENTILE", 264 e.this, 265 e.args.get("weight"), 266 e.args.get("quantile"), 267 e.args.get("accuracy"), 268 ), 269 exp.ArgMax: rename_func("MAX_BY"), 270 exp.ArgMin: rename_func("MIN_BY"), 271 exp.Array: transforms.preprocess( 272 [transforms.inherit_struct_field_names], 273 generator=lambda self, e: f"ARRAY[{self.expressions(e, flat=True)}]", 274 ), 275 exp.ArrayAny: rename_func("ANY_MATCH"), 276 exp.ArrayConcat: rename_func("CONCAT"), 277 exp.ArrayContains: rename_func("CONTAINS"), 278 exp.ArrayToString: rename_func("ARRAY_JOIN"), 279 exp.ArrayUniqueAgg: rename_func("SET_AGG"), 280 exp.ArraySlice: rename_func("SLICE"), 281 exp.AtTimeZone: rename_func("AT_TIMEZONE"), 282 exp.BitwiseAnd: lambda self, e: self.func("BITWISE_AND", e.this, e.expression), 283 exp.BitwiseLeftShift: rename_func("BITWISE_LEFT_SHIFT"), 284 exp.BitwiseNot: lambda self, e: self.func("BITWISE_NOT", e.this), 285 exp.BitwiseOr: lambda self, e: self.func("BITWISE_OR", e.this, e.expression), 286 exp.BitwiseRightShift: rename_func("BITWISE_RIGHT_SHIFT"), 287 exp.BitwiseXor: lambda self, e: self.func("BITWISE_XOR", e.this, e.expression), 288 exp.Cast: transforms.preprocess([transforms.epoch_cast_to_ts]), 289 exp.CurrentTime: lambda *_: "CURRENT_TIME", 290 exp.CurrentTimestamp: lambda *_: "CURRENT_TIMESTAMP", 291 exp.CurrentUser: lambda *_: "CURRENT_USER", 292 exp.DateAdd: _date_delta_sql("DATE_ADD"), 293 exp.DateDiff: lambda self, e: self.func("DATE_DIFF", unit_to_str(e), e.expression, e.this), 294 exp.DateStrToDate: datestrtodate_sql, 295 exp.DateToDi: lambda self, e: ( 296 f"CAST(DATE_FORMAT({self.sql(e, 'this')}, {type(self.dialect).DATEINT_FORMAT}) AS INT)" 297 ), 298 exp.DateSub: _date_delta_sql("DATE_ADD", negate_interval=True), 299 exp.DayOfWeek: lambda self, e: f"(({self.func('DAY_OF_WEEK', e.this)} % 7) + 1)", 300 exp.DayOfWeekIso: rename_func("DAY_OF_WEEK"), 301 exp.Decode: lambda self, e: encode_decode_sql(self, e, "FROM_UTF8"), 302 exp.DiToDate: lambda self, e: ( 303 f"CAST(DATE_PARSE(CAST({self.sql(e, 'this')} AS VARCHAR), {type(self.dialect).DATEINT_FORMAT}) AS DATE)" 304 ), 305 exp.Encode: lambda self, e: encode_decode_sql(self, e, "TO_UTF8"), 306 exp.FileFormatProperty: lambda self, e: f"format={self.sql(exp.Literal.string(e.name))}", 307 exp.First: _first_last_sql, 308 exp.FromISO8601Date: rename_func("FROM_ISO8601_DATE"), 309 exp.FromISO8601Timestamp: rename_func("FROM_ISO8601_TIMESTAMP"), 310 exp.FromTimeZone: lambda self, e: ( 311 f"WITH_TIMEZONE({self.sql(e, 'this')}, {self.sql(e, 'zone')}) AT TIME ZONE 'UTC'" 312 ), 313 exp.GenerateSeries: sequence_sql, 314 exp.GenerateDateArray: sequence_sql, 315 exp.If: if_sql(), 316 exp.ILike: no_ilike_sql, 317 exp.Initcap: _initcap_sql, 318 exp.Last: _first_last_sql, 319 exp.LastDay: lambda self, e: self.func("LAST_DAY_OF_MONTH", e.this), 320 exp.Lateral: _explode_to_unnest_sql, 321 exp.Left: left_to_substring_sql, 322 exp.Levenshtein: unsupported_args("ins_cost", "del_cost", "sub_cost", "max_dist")( 323 rename_func("LEVENSHTEIN_DISTANCE") 324 ), 325 exp.LogicalAnd: rename_func("BOOL_AND"), 326 exp.LogicalOr: rename_func("BOOL_OR"), 327 exp.Pivot: no_pivot_sql, 328 exp.Quantile: _quantile_sql, 329 exp.RegexpExtract: regexp_extract_sql, 330 exp.RegexpExtractAll: regexp_extract_sql, 331 exp.Right: right_to_substring_sql, 332 exp.Schema: _schema_sql, 333 exp.SchemaCommentProperty: lambda self, e: self.naked_property(e), 334 exp.Select: transforms.preprocess( 335 [ 336 transforms.eliminate_window_clause, 337 transforms.eliminate_qualify, 338 transforms.eliminate_distinct_on, 339 transforms.explode_projection_to_unnest(1), 340 transforms.eliminate_semi_and_anti_joins, 341 amend_exploded_column_table, 342 ] 343 ), 344 exp.SortArray: _no_sort_array, 345 exp.SqlSecurityProperty: lambda self, e: f"SECURITY {self.sql(e.this)}", 346 exp.StrPosition: lambda self, e: strposition_sql(self, e, supports_occurrence=True), 347 exp.StrToDate: lambda self, e: f"CAST({_str_to_time_sql(self, e)} AS DATE)", 348 exp.StrToMap: rename_func("SPLIT_TO_MAP"), 349 exp.StrToTime: _str_to_time_sql, 350 exp.StructExtract: struct_extract_sql, 351 exp.Table: transforms.preprocess([transforms.unnest_generate_series]), 352 exp.Timestamp: no_timestamp_sql, 353 exp.TimestampAdd: _date_delta_sql("DATE_ADD"), 354 exp.TimestampTrunc: timestamptrunc_sql(), 355 exp.TimeStrToDate: timestrtotime_sql, 356 exp.TimeStrToTime: timestrtotime_sql, 357 exp.TimeStrToUnix: lambda self, e: self.func( 358 "TO_UNIXTIME", self.func("DATE_PARSE", e.this, type(self.dialect).TIME_FORMAT) 359 ), 360 exp.TimeToStr: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 361 exp.TimeToUnix: rename_func("TO_UNIXTIME"), 362 exp.ToChar: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), 363 exp.TryCast: transforms.preprocess([transforms.epoch_cast_to_ts]), 364 exp.TsOrDiToDi: lambda self, e: ( 365 f"CAST(SUBSTR(REPLACE(CAST({self.sql(e, 'this')} AS VARCHAR), '-', ''), 1, 8) AS INT)" 366 ), 367 exp.TsOrDsAdd: _ts_or_ds_add_sql, 368 exp.TsOrDsDiff: _ts_or_ds_diff_sql, 369 exp.TsOrDsToDate: _ts_or_ds_to_date_sql, 370 exp.Unhex: rename_func("FROM_HEX"), 371 exp.UnixToStr: lambda self, e: ( 372 f"DATE_FORMAT(FROM_UNIXTIME({self.sql(e, 'this')}), {self.format_time(e)})" 373 ), 374 exp.UnixToTime: _unix_to_time_sql, 375 exp.UnixToTimeStr: lambda self, e: f"CAST(FROM_UNIXTIME({self.sql(e, 'this')}) AS VARCHAR)", 376 exp.VariancePop: rename_func("VAR_POP"), 377 exp.With: transforms.preprocess([transforms.add_recursive_cte_column_names]), 378 exp.WithinGroup: transforms.preprocess([transforms.remove_within_group_for_percentiles]), 379 # Note: Presto's TRUNCATE always returns DOUBLE, even with decimals=0, whereas 380 # most dialects return INT (SQLite also returns REAL, see sqlite.py). This creates 381 # a bidirectional transpilation gap: Presto→Other may change float division to int 382 # division, and vice versa. Modeling precisely would require exp.FloatTrunc or 383 # similar, deemed overengineering for this subtle semantic difference. 384 exp.Trunc: rename_func("TRUNCATE"), 385 exp.Xor: bool_xor_sql, 386 exp.MD5Digest: rename_func("MD5"), 387 exp.SHA: rename_func("SHA1"), 388 exp.SHA1Digest: rename_func("SHA1"), 389 exp.SHA2: sha256_sql, 390 exp.SHA2Digest: sha2_digest_sql, 391 exp.Substring: rename_func("SUBSTR"), 392 } 393 394 RESERVED_KEYWORDS = { 395 "alter", 396 "and", 397 "as", 398 "between", 399 "by", 400 "case", 401 "cast", 402 "constraint", 403 "create", 404 "cross", 405 "current_time", 406 "current_timestamp", 407 "deallocate", 408 "delete", 409 "describe", 410 "distinct", 411 "drop", 412 "else", 413 "end", 414 "escape", 415 "except", 416 "execute", 417 "exists", 418 "extract", 419 "false", 420 "for", 421 "from", 422 "full", 423 "group", 424 "having", 425 "in", 426 "inner", 427 "insert", 428 "intersect", 429 "into", 430 "is", 431 "join", 432 "left", 433 "like", 434 "natural", 435 "not", 436 "null", 437 "on", 438 "or", 439 "order", 440 "outer", 441 "prepare", 442 "right", 443 "select", 444 "table", 445 "then", 446 "true", 447 "union", 448 "using", 449 "values", 450 "when", 451 "where", 452 "with", 453 } 454 455 def extract_sql(self, expression: exp.Extract) -> str: 456 date_part = expression.name 457 458 if not date_part.startswith("EPOCH"): 459 return super().extract_sql(expression) 460 461 if date_part == "EPOCH_MILLISECOND": 462 scale = 10**3 463 elif date_part == "EPOCH_MICROSECOND": 464 scale = 10**6 465 elif date_part == "EPOCH_NANOSECOND": 466 scale = 10**9 467 else: 468 scale = None 469 470 value = expression.expression 471 472 ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr()) 473 to_unix: exp.Expr = exp.TimeToUnix(this=ts) 474 475 if scale: 476 to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale)) 477 478 return self.sql(to_unix) 479 480 def jsonformat_sql(self, expression: exp.JSONFormat) -> str: 481 this = expression.this 482 is_json = expression.args.get("is_json") 483 484 if this and not (is_json or this.type): 485 from sqlglot.optimizer.annotate_types import annotate_types 486 487 this = annotate_types(this, dialect=self.dialect) 488 489 if not (is_json or this.is_type(exp.DType.JSON)): 490 this.replace(exp.cast(this, exp.DType.JSON)) 491 492 return self.function_fallback_sql(expression) 493 494 def md5_sql(self, expression: exp.MD5) -> str: 495 this = expression.this 496 497 if not this.type: 498 from sqlglot.optimizer.annotate_types import annotate_types 499 500 this = annotate_types(this, dialect=self.dialect) 501 502 if this.is_type(*exp.DataType.TEXT_TYPES): 503 this = exp.Encode(this=this, charset=exp.Literal.string("utf-8")) 504 505 return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this)))) 506 507 def strtounix_sql(self, expression: exp.StrToUnix) -> str: 508 # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one. 509 # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a 510 # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback, 511 # which seems to be using the same time mapping as Hive, as per: 512 # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html 513 this = expression.this 514 value_as_text = exp.cast(this, exp.DType.TEXT) 515 value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this 516 517 parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression)) 518 519 formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression)) 520 parse_with_tz = self.func( 521 "PARSE_DATETIME", 522 formatted_value, 523 self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE), 524 ) 525 coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz) 526 return self.func("TO_UNIXTIME", coalesced) 527 528 def bracket_sql(self, expression: exp.Bracket) -> str: 529 if expression.args.get("safe"): 530 return bracket_to_element_at_sql(self, expression) 531 return super().bracket_sql(expression) 532 533 def struct_sql(self, expression: exp.Struct) -> str: 534 if not expression.type: 535 from sqlglot.optimizer.annotate_types import annotate_types 536 537 annotate_types(expression, dialect=self.dialect) 538 539 values: list[str] = [] 540 schema: list[str] = [] 541 unknown_type = False 542 543 for e in expression.expressions: 544 if isinstance(e, exp.PropertyEQ): 545 if e.type and e.type.is_type(exp.DType.UNKNOWN): 546 unknown_type = True 547 else: 548 schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}") 549 values.append(self.sql(e, "expression")) 550 else: 551 values.append(self.sql(e)) 552 553 size = len(expression.expressions) 554 555 if not size or len(schema) != size: 556 if unknown_type: 557 self.unsupported( 558 "Cannot convert untyped key-value definitions (try annotate_types)." 559 ) 560 return self.func("ROW", *values) 561 return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))" 562 563 def interval_sql(self, expression: exp.Interval) -> str: 564 if expression.this and expression.text("unit").upper().startswith("WEEK"): 565 return f"({expression.this.name} * INTERVAL '7' DAY)" 566 return super().interval_sql(expression) 567 568 def transaction_sql(self, expression: exp.Transaction) -> str: 569 modes = expression.args.get("modes") 570 modes = f" {', '.join(modes)}" if modes else "" 571 return f"START TRANSACTION{modes}" 572 573 def offset_limit_modifiers( 574 self, expression: exp.Expr, fetch: bool, limit: exp.Fetch | exp.Limit | None 575 ) -> list[str]: 576 return [ 577 self.sql(expression, "offset"), 578 self.sql(limit), 579 ] 580 581 def create_sql(self, expression: exp.Create) -> str: 582 """ 583 Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression), 584 so we need to remove them 585 """ 586 kind = expression.args["kind"] 587 schema = expression.this 588 if kind == "VIEW" and schema.expressions: 589 expression.this.set("expressions", None) 590 return super().create_sql(expression) 591 592 def delete_sql(self, expression: exp.Delete) -> str: 593 """ 594 Presto only supports DELETE FROM for a single table without an alias, so we need 595 to remove the unnecessary parts. If the original DELETE statement contains more 596 than one table to be deleted, we can't safely map it 1-1 to a Presto statement. 597 """ 598 tables = expression.args.get("tables") or [expression.this] 599 if len(tables) > 1: 600 return super().delete_sql(expression) 601 602 table = tables[0] 603 expression.set("this", table) 604 expression.set("tables", None) 605 606 if isinstance(table, exp.Table): 607 table_alias = table.args.get("alias") 608 if table_alias: 609 table_alias.pop() 610 expression = t.cast(exp.Delete, expression.transform(unqualify_columns)) 611 612 return super().delete_sql(expression) 613 614 def jsonextract_sql(self, expression: exp.JSONExtract) -> str: 615 is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True) 616 617 # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks 618 # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino 619 if not expression.args.get("variant_extract") or is_json_extract: 620 return self.func( 621 "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions 622 ) 623 624 this = self.sql(expression, "this") 625 626 # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y 627 segments = [] 628 for path_key in expression.expression.expressions[1:]: 629 if not isinstance(path_key, exp.JSONPathKey): 630 # Cannot transpile subscripts, wildcards etc to dot notation 631 self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access") 632 continue 633 key = path_key.this 634 if not exp.SAFE_IDENTIFIER_RE.match(key): 635 key = f'"{key}"' 636 segments.append(f".{key}") 637 638 expr = "".join(segments) 639 640 return f"{this}{expr}" 641 642 def groupconcat_sql(self, expression: exp.GroupConcat) -> str: 643 return self.func( 644 "ARRAY_JOIN", 645 self.func("ARRAY_AGG", expression.this), 646 expression.args.get("separator"), 647 )
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 PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateStrToDate'>: <function datestrtodate_sql>, <class 'sqlglot.expressions.temporal.DateToDi'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateSub'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.DayOfWeek'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DayOfWeekIso'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Decode'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DiToDate'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.string.Encode'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.properties.FileFormatProperty'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.First'>: <function _first_last_sql>, <class 'sqlglot.expressions.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.SHA2'>: <function sha256_sql>, <class 'sqlglot.expressions.string.SHA2Digest'>: <function sha2_digest_sql>, <class 'sqlglot.expressions.string.Substring'>: <function rename_func.<locals>.<lambda>>}
RESERVED_KEYWORDS =
{'case', 'with', 'between', 'prepare', 'values', 'except', 'end', 'union', 'escape', 'true', 'group', 'intersect', 'having', 'false', 'on', 'insert', 'exists', 'right', 'for', 'select', 'create', 'inner', 'when', 'into', 'constraint', 'in', 'distinct', 'left', 'or', 'from', 'join', 'full', 'delete', 'is', 'then', 'else', 'extract', 'natural', 'current_time', 'order', 'table', 'using', 'deallocate', 'and', 'like', 'alter', 'by', 'describe', 'drop', 'cross', 'not', 'cast', 'execute', 'null', 'as', 'where', 'current_timestamp', 'outer'}
455 def extract_sql(self, expression: exp.Extract) -> str: 456 date_part = expression.name 457 458 if not date_part.startswith("EPOCH"): 459 return super().extract_sql(expression) 460 461 if date_part == "EPOCH_MILLISECOND": 462 scale = 10**3 463 elif date_part == "EPOCH_MICROSECOND": 464 scale = 10**6 465 elif date_part == "EPOCH_NANOSECOND": 466 scale = 10**9 467 else: 468 scale = None 469 470 value = expression.expression 471 472 ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr()) 473 to_unix: exp.Expr = exp.TimeToUnix(this=ts) 474 475 if scale: 476 to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale)) 477 478 return self.sql(to_unix)
480 def jsonformat_sql(self, expression: exp.JSONFormat) -> str: 481 this = expression.this 482 is_json = expression.args.get("is_json") 483 484 if this and not (is_json or this.type): 485 from sqlglot.optimizer.annotate_types import annotate_types 486 487 this = annotate_types(this, dialect=self.dialect) 488 489 if not (is_json or this.is_type(exp.DType.JSON)): 490 this.replace(exp.cast(this, exp.DType.JSON)) 491 492 return self.function_fallback_sql(expression)
494 def md5_sql(self, expression: exp.MD5) -> str: 495 this = expression.this 496 497 if not this.type: 498 from sqlglot.optimizer.annotate_types import annotate_types 499 500 this = annotate_types(this, dialect=self.dialect) 501 502 if this.is_type(*exp.DataType.TEXT_TYPES): 503 this = exp.Encode(this=this, charset=exp.Literal.string("utf-8")) 504 505 return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this))))
507 def strtounix_sql(self, expression: exp.StrToUnix) -> str: 508 # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one. 509 # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a 510 # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback, 511 # which seems to be using the same time mapping as Hive, as per: 512 # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html 513 this = expression.this 514 value_as_text = exp.cast(this, exp.DType.TEXT) 515 value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this 516 517 parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression)) 518 519 formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression)) 520 parse_with_tz = self.func( 521 "PARSE_DATETIME", 522 formatted_value, 523 self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE), 524 ) 525 coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz) 526 return self.func("TO_UNIXTIME", coalesced)
533 def struct_sql(self, expression: exp.Struct) -> str: 534 if not expression.type: 535 from sqlglot.optimizer.annotate_types import annotate_types 536 537 annotate_types(expression, dialect=self.dialect) 538 539 values: list[str] = [] 540 schema: list[str] = [] 541 unknown_type = False 542 543 for e in expression.expressions: 544 if isinstance(e, exp.PropertyEQ): 545 if e.type and e.type.is_type(exp.DType.UNKNOWN): 546 unknown_type = True 547 else: 548 schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}") 549 values.append(self.sql(e, "expression")) 550 else: 551 values.append(self.sql(e)) 552 553 size = len(expression.expressions) 554 555 if not size or len(schema) != size: 556 if unknown_type: 557 self.unsupported( 558 "Cannot convert untyped key-value definitions (try annotate_types)." 559 ) 560 return self.func("ROW", *values) 561 return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))"
def
offset_limit_modifiers( self, expression: sqlglot.expressions.core.Expr, fetch: bool, limit: sqlglot.expressions.query.Fetch | sqlglot.expressions.query.Limit | None) -> list[str]:
581 def create_sql(self, expression: exp.Create) -> str: 582 """ 583 Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression), 584 so we need to remove them 585 """ 586 kind = expression.args["kind"] 587 schema = expression.this 588 if kind == "VIEW" and schema.expressions: 589 expression.this.set("expressions", None) 590 return super().create_sql(expression)
Presto doesn't support CREATE VIEW with expressions (ex: CREATE VIEW x (cola) then (cola) is the expression),
so we need to remove them
592 def delete_sql(self, expression: exp.Delete) -> str: 593 """ 594 Presto only supports DELETE FROM for a single table without an alias, so we need 595 to remove the unnecessary parts. If the original DELETE statement contains more 596 than one table to be deleted, we can't safely map it 1-1 to a Presto statement. 597 """ 598 tables = expression.args.get("tables") or [expression.this] 599 if len(tables) > 1: 600 return super().delete_sql(expression) 601 602 table = tables[0] 603 expression.set("this", table) 604 expression.set("tables", None) 605 606 if isinstance(table, exp.Table): 607 table_alias = table.args.get("alias") 608 if table_alias: 609 table_alias.pop() 610 expression = t.cast(exp.Delete, expression.transform(unqualify_columns)) 611 612 return super().delete_sql(expression)
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.
614 def jsonextract_sql(self, expression: exp.JSONExtract) -> str: 615 is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True) 616 617 # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks 618 # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino 619 if not expression.args.get("variant_extract") or is_json_extract: 620 return self.func( 621 "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions 622 ) 623 624 this = self.sql(expression, "this") 625 626 # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y 627 segments = [] 628 for path_key in expression.expression.expressions[1:]: 629 if not isinstance(path_key, exp.JSONPathKey): 630 # Cannot transpile subscripts, wildcards etc to dot notation 631 self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access") 632 continue 633 key = path_key.this 634 if not exp.SAFE_IDENTIFIER_RE.match(key): 635 key = f'"{key}"' 636 segments.append(f".{key}") 637 638 expr = "".join(segments) 639 640 return f"{this}{expr}"
Inherited Members
- sqlglot.generator.Generator
- Generator
- NULL_ORDERING_SUPPORTED
- WINDOW_FUNCS_WITH_NULL_ORDERING
- IGNORE_NULLS_IN_FUNC
- IGNORE_NULLS_BEFORE_ORDER
- LOCKING_READS_SUPPORTED
- WRAP_DERIVED_VALUES
- CREATE_FUNCTION_RETURN_AS
- MATCHED_BY_SOURCE
- SUPPORTS_MERGE_WHERE
- SINGLE_STRING_INTERVAL
- LIMIT_FETCH
- RENAME_TABLE_WITH_DB
- GROUPINGS_SEP
- INDEX_ON
- INOUT_SEPARATOR
- DIRECTED_JOINS
- QUERY_HINT_SEP
- DUPLICATE_KEY_UPDATE_WITH_SET
- LIMIT_IS_TOP
- RETURNING_END
- EXTRACT_ALLOWS_QUOTES
- VALUES_AS_TABLE
- ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
- UNNEST_WITH_ORDINALITY
- AGGREGATE_FILTER_SUPPORTED
- SEMI_ANTI_JOIN_WITH_SIDE
- COMPUTED_COLUMN_WITH_TYPE
- SUPPORTS_TABLE_COPY
- TABLESAMPLE_REQUIRES_PARENS
- TABLESAMPLE_SIZE_IS_ROWS
- TABLESAMPLE_KEYWORDS
- TABLESAMPLE_WITH_METHOD
- TABLESAMPLE_SEED_KEYWORD
- COLLATE_IS_FUNC
- DATA_TYPE_SPECIFIERS_ALLOWED
- ENSURE_BOOLS
- CTE_RECURSIVE_KEYWORD_REQUIRED
- LAST_DAY_SUPPORTS_DATE_PART
- SUPPORTS_TABLE_ALIAS_COLUMNS
- SUPPORTS_NAMED_CTE_COLUMNS
- UNPIVOT_ALIASES_ARE_IDENTIFIERS
- 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
- 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
- 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
- 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_sql
- chr_sql
- block_sql
- storedprocedure_sql
- ifblock_sql
- whileblock_sql
- execute_sql
- executesql_sql
- altermodifysqlsecurity_sql
- usingproperty_sql
- renameindex_sql