Edit on GitHub

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

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 WHERE clause. 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
SELECT_KINDS: tuple[str, ...] = ()
SUPPORTS_DECODE_CASE = False
SUPPORTS_TEMPORAL_PRECISION = False
AFTER_HAVING_MODIFIER_TRANSFORMS = {'windows': <function <lambda>>, 'qualify': <function <lambda>>}
INTERVAL_ALLOWS_PLURAL_FORM = False
JOIN_HINTS = False
CAST_TO_INT_ROUNDS = True
TABLE_HINTS = False
QUERY_HINTS = False
IS_BOOL_ALLOWED = False
TZ_TO_WITH_TIME_ZONE = True
NVL2_SUPPORTED = False
STRUCT_DELIMITER = ('(', ')')
LIMIT_ONLY_LITERALS = True
SUPPORTS_SINGLE_ARG_CONCAT = False
LIKE_PROPERTY_INSIDE_SCHEMA = True
MULTI_ARG_DISTINCT = False
SUPPORTS_TO_NUMBER = False
HEX_FUNC = 'TO_HEX'
PARSE_JSON_NAME: str | None = 'JSON_PARSE'
PAD_FILL_PATTERN_IS_REQUIRED = True
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = False
SUPPORTS_MEDIAN = False
ARRAY_SIZE_NAME = 'CARDINALITY'
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.properties.AggregateKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AllowedValuesProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AlgorithmProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApiProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApplicationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.AutoIncrementProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AutoRefreshProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BackupProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BlockCompressionProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CalledOnNullInputProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.CatalogProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ChecksumProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CollateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ComputeProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.CopyTagsProperty'>: <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.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.DistStyleProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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.IgnoreGrantsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IncludeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.InputModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.InteractiveProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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.properties.OptionsProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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.properties.PhysicalProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.constraints.PrimaryKey'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Property'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.RefreshTriggerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RollupProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowAccessProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowFormatProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatDelimitedProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatSerdeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SampleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SchemaCommentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SecureProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SecurityIntegrationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SerdeProperties'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.Set'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SettingsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SetProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SetConfigProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SharingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.SequenceProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.TriggerProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.SortKeyProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.SqlReadWriteProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlSecurityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StabilityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StorageHandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StorageProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.StreamingTableProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.StrictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Tags'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.TableSpaceProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.TemporaryProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ToTableProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.TransientProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.TransformModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.MergeTreeTTL'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.UnloggedProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.UsingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.UsingTemplateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ViewAttributeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.VirtualProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.VolatileProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.WithDataProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.WithJournalTableProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.WithProcedureOptions'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSchemaBindingProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSystemVersioningProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithoutRowidProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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.BinaryColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CalledOnNullInputProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.math.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.string.ConvertToCharset'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CopyTagsProperty'>: <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.IgnoreGrantsProperty'>: <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.JSONBContainsTopKey'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBDeleteAtPath'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBPathExists'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONObject'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONObjectAgg'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.MaskingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.NetFunc'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.NetworkProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.ExtendsLeft'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.ExtendsRight'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionedByBucket'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.PartitionByTruncate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.core.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.PositionalColumn'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.InvisibleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ZeroFillColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Put'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <function 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.WithoutRowidProperty'>: <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.ArraySort'>: <function _array_sort_sql>, <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 array_slice_to_slice_sql>, <class 'sqlglot.expressions.core.AtTimeZone'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.BitwiseAnd'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseLeftShift'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.BitwiseNot'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseOr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.core.BitwiseRightShift'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.BitwiseXor'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.functions.Cast'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.CurrentTime'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.CurrentTimestamp'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.functions.CurrentUser'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateAdd'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.DateDiff'>: <function _date_diff_sql>, <class 'sqlglot.expressions.temporal.DatetimeDiff'>: <function _date_diff_sql>, <class 'sqlglot.expressions.temporal.TimestampDiff'>: <function _date_diff_sql>, <class 'sqlglot.expressions.temporal.DateStrToDate'>: <function datestrtodate_sql>, <class 'sqlglot.expressions.temporal.DateToDi'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DateSub'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.DayOfWeek'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DayOfWeekIso'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Decode'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.DiToDate'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.string.Encode'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.properties.FileFormatProperty'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.First'>: <function _first_last_sql>, <class 'sqlglot.expressions.temporal.FromISO8601Date'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.FromISO8601Timestamp'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.FromTimeZone'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.array.GenerateSeries'>: <function sequence_sql>, <class 'sqlglot.expressions.temporal.GenerateDateArray'>: <function sequence_sql>, <class 'sqlglot.expressions.functions.If'>: <function if_sql.<locals>._if_sql>, <class 'sqlglot.expressions.core.ILike'>: <function no_ilike_sql>, <class 'sqlglot.expressions.string.Initcap'>: <function _initcap_sql>, <class 'sqlglot.expressions.aggregate.Last'>: <function _first_last_sql>, <class 'sqlglot.expressions.temporal.LastDay'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.query.Lateral'>: <function _explode_to_unnest_sql>, <class 'sqlglot.expressions.string.Left'>: <function left_to_substring_sql>, <class 'sqlglot.expressions.string.Levenshtein'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.LogicalAnd'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.LogicalOr'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.Pivot'>: <function no_pivot_sql>, <class 'sqlglot.expressions.aggregate.Quantile'>: <function _quantile_sql>, <class 'sqlglot.expressions.string.RegexpExtract'>: <function regexp_extract_sql>, <class 'sqlglot.expressions.string.RegexpExtractAll'>: <function regexp_extract_sql>, <class 'sqlglot.expressions.string.Right'>: <function right_to_substring_sql>, <class 'sqlglot.expressions.query.Schema'>: <function _schema_sql>, <class 'sqlglot.expressions.properties.SchemaCommentProperty'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.query.Select'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.string.StrPosition'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.StrToDate'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.string.StrToMap'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.StrToTime'>: <function _str_to_time_sql>, <class 'sqlglot.expressions.array.StructExtract'>: <function struct_extract_sql>, <class 'sqlglot.expressions.query.Table'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.Timestamp'>: <function no_timestamp_sql>, <class 'sqlglot.expressions.temporal.TimestampAdd'>: <function _date_delta_sql.<locals>._delta_sql>, <class 'sqlglot.expressions.temporal.TimestampTrunc'>: <function timestamptrunc_sql.<locals>._timestamptrunc_sql>, <class 'sqlglot.expressions.temporal.TimeStrToDate'>: <function timestrtotime_sql>, <class 'sqlglot.expressions.temporal.TimeStrToTime'>: <function timestrtotime_sql>, <class 'sqlglot.expressions.temporal.TimeStrToUnix'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TimeToStr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TimeToUnix'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.ToChar'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.functions.TryCast'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.temporal.TsOrDiToDi'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TsOrDsAdd'>: <function _ts_or_ds_add_sql>, <class 'sqlglot.expressions.temporal.TsOrDsDiff'>: <function _ts_or_ds_diff_sql>, <class 'sqlglot.expressions.temporal.TsOrDsToDate'>: <function _ts_or_ds_to_date_sql>, <class 'sqlglot.expressions.string.Unhex'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.UnixToStr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.UnixToTime'>: <function _unix_to_time_sql>, <class 'sqlglot.expressions.temporal.UnixToTimeStr'>: <function PrestoGenerator.<lambda>>, <class 'sqlglot.expressions.aggregate.VariancePop'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.With'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.core.WithinGroup'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.math.Trunc'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.core.Xor'>: <function bool_xor_sql>, <class 'sqlglot.expressions.string.MD5Digest'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA1Digest'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.SHA2Digest'>: <function _sha2_digest_sql>, <class 'sqlglot.expressions.string.Substring'>: <function rename_func.<locals>.<lambda>>}
RESERVED_KEYWORDS = {'on', 'insert', 'intersect', 'inner', 'false', 'join', 'then', 'left', 'order', 'else', 'using', 'table', 'current_timestamp', 'delete', 'is', 'true', 'execute', 'in', 'escape', 'values', 'cross', 'except', 'natural', 'into', 'end', 'distinct', 'exists', 'full', 'like', 'outer', 'union', 'when', 'deallocate', 'drop', 'having', 'where', 'extract', 'alter', 'constraint', 'with', 'current_time', 'create', 'cast', 'null', 'select', 'or', 'by', 'right', 'between', 'group', 'not', 'as', 'prepare', 'case', 'for', 'and', 'describe', 'from'}
def datatype_sql(self, expression: sqlglot.expressions.datatypes.DataType) -> str:
513    def datatype_sql(self, expression: exp.DataType) -> str:
514        if not self.SUPPORTS_TEMPORAL_PRECISION and expression.this in exp.DataType.TEMPORAL_TYPES:
515            if expression.expressions and expression.expressions[0].name != "3":
516                self.unsupported("Presto only supports millisecond precision for temporal types")
517            expression.set("expressions", None)
518        return super().datatype_sql(expression)
def extract_sql(self, expression: sqlglot.expressions.temporal.Extract) -> str:
520    def extract_sql(self, expression: exp.Extract) -> str:
521        date_part = expression.name
522
523        if not date_part.startswith("EPOCH"):
524            return super().extract_sql(expression)
525
526        if date_part == "EPOCH_MILLISECOND":
527            scale = 10**3
528        elif date_part == "EPOCH_MICROSECOND":
529            scale = 10**6
530        elif date_part == "EPOCH_NANOSECOND":
531            scale = 10**9
532        else:
533            scale = None
534
535        value = expression.expression
536
537        ts = exp.cast(value, to=exp.DType.TIMESTAMP.into_expr())
538        to_unix: exp.Expr = exp.TimeToUnix(this=ts)
539
540        if scale:
541            to_unix = exp.Mul(this=to_unix, expression=exp.Literal.number(scale))
542
543        return self.sql(to_unix)
def jsonformat_sql(self, expression: sqlglot.expressions.json.JSONFormat) -> str:
545    def jsonformat_sql(self, expression: exp.JSONFormat) -> str:
546        this = expression.this
547        is_json = expression.args.get("is_json")
548
549        if this and not (is_json or this.type):
550            from sqlglot.optimizer.annotate_types import annotate_types
551
552            this = annotate_types(this, dialect=self.dialect)
553
554        if not (is_json or this.is_type(exp.DType.JSON)):
555            this.replace(exp.cast(this, exp.DType.JSON))
556
557        return self.function_fallback_sql(expression)
def md5_sql(self, expression: sqlglot.expressions.string.MD5) -> str:
559    def md5_sql(self, expression: exp.MD5) -> str:
560        this = expression.this
561
562        if not this.type:
563            from sqlglot.optimizer.annotate_types import annotate_types
564
565            this = annotate_types(this, dialect=self.dialect)
566
567        if this.is_type(*exp.DataType.TEXT_TYPES):
568            this = exp.Encode(this=this, charset=exp.Literal.string("utf-8"))
569
570        return self.func("LOWER", self.func("TO_HEX", self.func("MD5", self.sql(this))))
def sha2_sql(self, expression: sqlglot.expressions.string.SHA2) -> str:
572    def sha2_sql(self, expression: exp.SHA2) -> str:
573        length = expression.text("length") or "256"
574        if length not in ("256", "512"):
575            self.unsupported(f"SHA{length} is not supported in Presto")
576
577        this = expression.this
578
579        if this.is_type(*exp.DataType.TEXT_TYPES):
580            this = exp.Encode(this=this, charset=exp.Literal.string("utf-8"))
581
582        return self.func("LOWER", self.func("TO_HEX", self.func(f"SHA{length}", self.sql(this))))
def strtounix_sql(self, expression: sqlglot.expressions.temporal.StrToUnix) -> str:
584    def strtounix_sql(self, expression: exp.StrToUnix) -> str:
585        # Since `TO_UNIXTIME` requires a `TIMESTAMP`, we need to parse the argument into one.
586        # To do this, we first try to `DATE_PARSE` it, but since this can fail when there's a
587        # timezone involved, we wrap it in a `TRY` call and use `PARSE_DATETIME` as a fallback,
588        # which seems to be using the same time mapping as Hive, as per:
589        # https://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html
590        this = expression.this
591        value_as_text = exp.cast(this, exp.DType.TEXT)
592        value_as_timestamp = exp.cast(this, exp.DType.TIMESTAMP) if this.is_string else this
593
594        parse_without_tz = self.func("DATE_PARSE", value_as_text, self.format_time(expression))
595
596        formatted_value = self.func("DATE_FORMAT", value_as_timestamp, self.format_time(expression))
597        parse_with_tz = self.func(
598            "PARSE_DATETIME",
599            formatted_value,
600            self.format_time(expression, Hive.INVERSE_TIME_MAPPING, Hive.INVERSE_TIME_TRIE),
601        )
602        coalesced = self.func("COALESCE", self.func("TRY", parse_without_tz), parse_with_tz)
603        return self.func("TO_UNIXTIME", coalesced)
def bracket_sql(self, expression: sqlglot.expressions.core.Bracket) -> str:
605    def bracket_sql(self, expression: exp.Bracket) -> str:
606        if expression.args.get("safe"):
607            return bracket_to_element_at_sql(self, expression)
608        return super().bracket_sql(expression)
def struct_sql(self, expression: sqlglot.expressions.array.Struct) -> str:
610    def struct_sql(self, expression: exp.Struct) -> str:
611        if not expression.type:
612            from sqlglot.optimizer.annotate_types import annotate_types
613
614            annotate_types(expression, dialect=self.dialect)
615
616        values: list[str] = []
617        schema: list[str] = []
618        unknown_type = False
619
620        for e in expression.expressions:
621            if isinstance(e, exp.PropertyEQ):
622                if e.type and e.type.is_type(exp.DType.UNKNOWN):
623                    unknown_type = True
624                else:
625                    schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}")
626                values.append(self.sql(e, "expression"))
627            else:
628                values.append(self.sql(e))
629
630        size = len(expression.expressions)
631
632        if not size or len(schema) != size:
633            if unknown_type:
634                self.unsupported(
635                    "Cannot convert untyped key-value definitions (try annotate_types)."
636                )
637            return self.func("ROW", *values)
638        return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))"
def interval_sql(self, expression: sqlglot.expressions.datatypes.Interval) -> str:
640    def interval_sql(self, expression: exp.Interval) -> str:
641        this = expression.this
642        if this and expression.text("unit").upper().startswith("WEEK"):
643            if not this.is_string:
644                return f"({self.sql(this)} * INTERVAL '7' DAY)"
645            if is_float(this.name):
646                return f"({this.name} * INTERVAL '7' DAY)"
647        return super().interval_sql(expression)
def transaction_sql(self, expression: sqlglot.expressions.ddl.Transaction) -> str:
649    def transaction_sql(self, expression: exp.Transaction) -> str:
650        modes = expression.args.get("modes")
651        modes = f" {', '.join(modes)}" if modes else ""
652        return f"START TRANSACTION{modes}"
def offset_limit_modifiers( self, expression: sqlglot.expressions.core.Expr, fetch: bool, limit: sqlglot.expressions.query.Fetch | sqlglot.expressions.query.Limit | None) -> list[str]:
654    def offset_limit_modifiers(
655        self, expression: exp.Expr, fetch: bool, limit: exp.Fetch | exp.Limit | None
656    ) -> list[str]:
657        return [
658            self.sql(expression, "offset"),
659            self.sql(limit),
660        ]
def create_sql(self, expression: sqlglot.expressions.ddl.Create) -> str:
662    def create_sql(self, expression: exp.Create) -> str:
663        """
664        Presto doesn't support CREATE VIEW with expressions (ex: `CREATE VIEW x (cola)` then `(cola)` is the expression),
665        so we need to remove them
666        """
667        kind = expression.args["kind"]
668        schema = expression.this
669        if kind == "VIEW" and schema.expressions:
670            expression.this.set("expressions", None)
671        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

def delete_sql(self, expression: sqlglot.expressions.dml.Delete) -> str:
673    def delete_sql(self, expression: exp.Delete) -> str:
674        """
675        Presto only supports DELETE FROM for a single table without an alias, so we need
676        to remove the unnecessary parts. If the original DELETE statement contains more
677        than one table to be deleted, we can't safely map it 1-1 to a Presto statement.
678        """
679        tables = expression.args.get("tables") or [expression.this]
680        if len(tables) > 1:
681            return super().delete_sql(expression)
682
683        table = tables[0]
684        expression.set("this", table)
685        expression.set("tables", None)
686
687        if isinstance(table, exp.Table):
688            table_alias = table.args.get("alias")
689            if table_alias:
690                table_alias.pop()
691                expression = t.cast(exp.Delete, expression.transform(unqualify_columns))
692
693        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.

def jsonextract_sql(self, expression: sqlglot.expressions.json.JSONExtract) -> str:
695    def jsonextract_sql(self, expression: exp.JSONExtract) -> str:
696        is_json_extract = self.dialect.settings.get("variant_extract_is_json_extract", True)
697
698        # Generate JSON_EXTRACT unless the user has configured that a Snowflake / Databricks
699        # VARIANT extract (e.g. col:x.y) should map to dot notation (i.e ROW access) in Presto/Trino
700        if not expression.args.get("variant_extract") or is_json_extract:
701            return self.func(
702                "JSON_EXTRACT", expression.this, expression.expression, *expression.expressions
703            )
704
705        this = self.sql(expression, "this")
706
707        # Convert the JSONPath extraction `JSON_EXTRACT(col, '$.x.y) to a ROW access col.x.y
708        segments = []
709        for path_key in expression.expression.expressions[1:]:
710            if not isinstance(path_key, exp.JSONPathKey):
711                # Cannot transpile subscripts, wildcards etc to dot notation
712                self.unsupported(f"Cannot transpile JSONPath segment '{path_key}' to ROW access")
713                continue
714            key = path_key.this
715            if not exp.SAFE_IDENTIFIER_RE.match(key):
716                key = f'"{key}"'
717            segments.append(f".{key}")
718
719        expr = "".join(segments)
720
721        return f"{this}{expr}"
def groupconcat_sql(self, expression: sqlglot.expressions.aggregate.GroupConcat) -> str:
723    def groupconcat_sql(self, expression: exp.GroupConcat) -> str:
724        return self.func(
725            "ARRAY_JOIN",
726            self.func("ARRAY_AGG", expression.this),
727            expression.args.get("separator"),
728        )
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
MERGE_ALL_BY_NAME
SINGLE_STRING_INTERVAL
AUTO_REFRESH_BARE_INTERVALS
LIMIT_FETCH
RENAME_TABLE_WITH_DB
GROUPINGS_SEP
SUPPORTS_GROUPING_SETS_AS_SUFFIX
INDEX_ON
INOUT_SEPARATOR
DIRECTED_JOINS
SUPPORTS_NEAREST_JOIN
QUERY_HINT_SEP
SAFE_INT_DIVISION
DUPLICATE_KEY_UPDATE_WITH_SET
LIMIT_IS_TOP
RETURNING_END
EXTRACT_ALLOWS_QUOTES
VALUES_AS_TABLE
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
ALTER_TABLE_ADD_COLUMNS_WRAPPED
UNNEST_WITH_ORDINALITY
SEMI_ANTI_JOIN_WITH_SIDE
COMPUTED_COLUMN_WITH_TYPE
SUPPORTS_TABLE_COPY
TABLESAMPLE_REQUIRES_PARENS
TABLESAMPLE_SIZE_IS_ROWS
TABLESAMPLE_KEYWORDS
TABLESAMPLE_WITH_METHOD
TABLESAMPLE_SEED_KEYWORD
HISTORICAL_DATA_POST_ALIAS
COLLATE_IS_FUNC
DATA_TYPE_SPECIFIERS_ALLOWED
ENSURE_BOOLS
CTE_RECURSIVE_KEYWORD_REQUIRED
LAST_DAY_SUPPORTS_DATE_PART
SUPPORTS_TABLE_ALIAS_COLUMNS
SUPPORTS_NAMED_CTE_COLUMNS
UNPIVOT_ALIASES_ARE_IDENTIFIERS
PIVOT_ALIAS_WITH_AS
JSON_KEY_VALUE_PAIR_SEP
INSERT_OVERWRITE
SUPPORTS_SELECT_INTO
SUPPORTS_UNLOGGED_TABLES
SUPPORTS_CREATE_TABLE_LIKE
SUPPORTS_MODIFY_COLUMN
SUPPORTS_CHANGE_COLUMN
SUPPORTS_ALTER_COLUMN_NULLABILITY
SUPPORTS_ALTER_COLUMN_IF_EXISTS
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
SET_OP_LIMITS
SET_OP_PARENTHESIZED_OPERANDS
COPY_PARAMS_ARE_WRAPPED
COPY_PARAMS_EQ_REQUIRED
COPY_HAS_INTO_KEYWORD
TRY_SUPPORTED
SUPPORTS_UESCAPE
UNICODE_SUBSTITUTE
STAR_EXCEPT
BYTE_STRING_FUNCTION
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
MOD_OPERATOR
MOD_PAREN_PARENT_TYPES
ALTER_PARTITION_OPTIONS
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
directory_sql
drop_sql
set_operation
set_operations
fetch_sql
limitoptions_sql
filter_sql
per_sql
hint_sql
indexparameters_sql
index_sql
dynamicidentifier_sql
identifier_sql
hex_sql
lowerhex_sql
inputoutputformat_sql
national_sql
partition_sql
properties_sql
root_properties
properties
with_properties
locate_properties
property_name
property_sql
uuidproperty_sql
likeproperty_sql
fallbackproperty_sql
journalproperty_sql
freespaceproperty_sql
checksumproperty_sql
mergeblockratioproperty_sql
moduleproperty_sql
datablocksizeproperty_sql
blockcompressionproperty_sql
isolatedloadingproperty_sql
partitionboundspec_sql
partitionedofproperty_sql
lockingproperty_sql
lockingstatement_sql
withdataproperty_sql
withsystemversioningproperty_sql
insert_sql
introducer_sql
kill_sql
pseudotype_sql
objectidentifier_sql
onconflict_sql
returning_sql
rowformatdelimitedproperty_sql
withtablehint_sql
indextablehint_sql
historicaldata_sql
table_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
connectto_sql
disconnect_sql
connect_sql
prior_sql
join_sql
nearest_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
partitionbyexcluding_sql
windowspec_sql
withingroup_sql
between_sql
bracket_offset_expressions
all_sql
any_sql
exists_sql
case_sql
constraint_sql
nextvaluefor_sql
trim_sql
convert_concat_args
concat_sql
concatws_sql
check_sql
foreignkey_sql
primarykey_sql
timeserieskey_sql
if_sql
matchagainst_sql
jsonkeyvalue_sql
jsonpath_sql
json_path_part
formatjson_sql
formatphrase_sql
teradatatitle_sql
jsonarray_sql
jsonarrayagg_sql
jsoncolumndef_sql
jsonschema_sql
jsontable_sql
openjsoncolumndef_sql
openjson_sql
in_sql
in_unnest_op
return_sql
reference_sql
anonymous_sql
paren_sql
neg_sql
not_sql
alias_sql
pivotalias_sql
aliases_sql
atindex_sql
attimezone_sql
fromtimezone_sql
fromiso8601date_sql
fromiso8601timestamp_sql
fromiso8601timestampnanos_sql
add_sql
and_sql
or_sql
xor_sql
connector_sql
bitwiseand_sql
bitwiseleftshift_sql
bitwisenot_sql
bitwiseor_sql
bitwiserightshift_sql
bitwisexor_sql
cast_sql
strtotime_sql
strtodate_sql
parsedatetime_sql
currentdate_sql
collate_sql
command_sql
comment_sql
mergetreettlaction_sql
mergetreettl_sql
commit_sql
rollback_sql
altercolumn_sql
modifycolumn_sql
alterindex_sql
alterdiststyle_sql
altersortkey_sql
alterrename_sql
renamecolumn_sql
alterset_sql
alter_sql
altersession_sql
add_column_sql
droppartition_sql
dropprimarykey_sql
addconstraint_sql
replacecolumns_sql
addpartition_sql
distinct_sql
ignorenulls_sql
respectnulls_sql
havingmax_sql
intdiv_sql
dpipe_sql
nullif_zero_divisor
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
function_name
func
format_args
too_wide
format_time
expressions
op_expressions
naked_property
tag_sql
token_sql
userdefinedfunction_sql
macrooverloads_sql
macrooverload_sql
joinhint_sql
kwarg_sql
when_sql
whens_sql
merge_sql
tochar_sql
tonumber_sql
dictproperty_sql
dictrange_sql
dictsubproperty_sql
aggregatekeyproperty_sql
duplicatekeyproperty_sql
uniquekeyproperty_sql
distributedbyproperty_sql
oncluster_sql
clusteredbyproperty_sql
anyvalue_sql
querytransform_sql
indexconstraintoption_sql
checkcolumnconstraint_sql
indexcolumnconstraint_sql
nvl2_sql
nthvalue_sql
comprehension_sql
columnprefix_sql
opclass_sql
predict_sql
generateembedding_sql
generatetext_sql
generatetable_sql
generatebool_sql
generateint_sql
generatedouble_sql
mltranslate_sql
mlforecast_sql
aiforecast_sql
featuresattime_sql
vectorsearch_sql
forin_sql
refresh_sql
toarray_sql
tsordstotime_sql
tsordstotimestamp_sql
tsordstodatetime_sql
tsordstodate_sql
unixdate_sql
lastday_sql
dateadd_sql
arrayinsert_sql
arrayany_sql
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
min_sql
max_sql
arrayagg_sql
timeslice_sql
slice_sql
apply_sql
grant_sql
revoke_sql
grantprivilege_sql
grantprincipal_sql
columns_sql
overlay_sql
todouble_sql
string_sql
median_sql
overflowtruncatebehavior_sql
unixseconds_sql
arraysort_sql
arraysize_sql
attach_sql
detach_sql
attachoption_sql
watermarkcolumnconstraint_sql
encodeproperty_sql
includeproperty_sql
xmlelement_sql
xmlkeyvalueoption_sql
partitionbyrangeproperty_sql
partitionbyrangepropertydynamic_sql
unpivotcolumns_sql
analyzesample_sql
analyzestatistics_sql
analyzehistogram_sql
analyzedelete_sql
analyzelistchainedrows_sql
analyzevalidate_sql
analyze_sql
xmltable_sql
xmlnamespace_sql
export_sql
declare_sql
declareitem_sql
recursivewithsearch_sql
parameterizedagg_sql
anonymousaggfunc_sql
combinedaggfunc_sql
combinedparameterizedagg_sql
show_sql
install_sql
liststage_sql
get_put_sql
translatecharacters_sql
decodecase_sql
semanticview_sql
getextract_sql
datefromunixdate_sql
space_sql
buildproperty_sql
refreshtriggerproperty_sql
modelattribute_sql
directorystage_sql
uuid_sql
initcap_sql
localtime_sql
localtimestamp_sql
weekstart_name
weekstart_sql
chr_sql
weightstring_sql
block_sql
functionspecification_sql
storedprocedure_sql
ifblock_sql
casestatement_sql
whileblock_sql
loopblock_sql
repeatblock_sql
leave_sql
iterate_sql
execute_sql
executesql_sql
altermodifysqlsecurity_sql
usingproperty_sql
renameindex_sql