Edit on GitHub

sqlglot.generators.mysql

  1from __future__ import annotations
  2
  3import typing as t
  4
  5from sqlglot import exp, generator, transforms
  6from sqlglot.dialects.dialect import (
  7    arrow_json_extract_sql,
  8    build_date_delta,
  9    build_date_delta_with_interval,
 10    date_add_interval_sql,
 11    datestrtodate_sql,
 12    length_or_char_length_sql,
 13    max_or_greatest,
 14    min_or_least,
 15    no_ilike_sql,
 16    no_paren_current_date_sql,
 17    no_pivot_sql,
 18    no_tablesample_sql,
 19    no_trycast_sql,
 20    if_sql,
 21    remove_ts_or_ds_to_date,
 22    rename_func,
 23    strposition_sql,
 24    unit_to_var,
 25    trim_sql,
 26    timestrtotime_sql,
 27)
 28from sqlglot.generator import unsupported_args
 29from collections import defaultdict
 30
 31
 32def _date_trunc_sql(self: MySQLGenerator, expression: exp.DateTrunc) -> str:
 33    expr = self.sql(expression, "this")
 34    unit_expr = expression.args.get("unit")
 35    unit = (
 36        self.weekstart_name(unit_expr)
 37        if isinstance(unit_expr, exp.WeekStart)
 38        else expression.text("unit").upper()
 39    )
 40
 41    if unit == "WEEK":
 42        concat = f"CONCAT(YEAR({expr}), ' ', WEEK({expr}, 1), ' 1')"
 43        date_format = "%Y %u %w"
 44    elif unit == "MONTH":
 45        concat = f"CONCAT(YEAR({expr}), ' ', MONTH({expr}), ' 1')"
 46        date_format = "%Y %c %e"
 47    elif unit == "QUARTER":
 48        concat = f"CONCAT(YEAR({expr}), ' ', QUARTER({expr}) * 3 - 2, ' 1')"
 49        date_format = "%Y %c %e"
 50    elif unit == "YEAR":
 51        concat = f"CONCAT(YEAR({expr}), ' 1 1')"
 52        date_format = "%Y %c %e"
 53    else:
 54        if unit != "DAY":
 55            self.unsupported(f"Unexpected interval unit: {unit}")
 56        return self.func("DATE", expr)
 57
 58    return self.func("STR_TO_DATE", concat, f"'{date_format}'")
 59
 60
 61def _str_to_date_sql(
 62    self: MySQLGenerator, expression: exp.StrToDate | exp.StrToTime | exp.TsOrDsToDate
 63) -> str:
 64    return self.func("STR_TO_DATE", expression.this, self.format_time(expression))
 65
 66
 67def _unix_to_time_sql(self: MySQLGenerator, expression: exp.UnixToTime) -> str:
 68    scale = expression.args.get("scale")
 69    timestamp = expression.this
 70
 71    if scale in (None, exp.UnixToTime.SECONDS):
 72        return self.func("FROM_UNIXTIME", timestamp, self.format_time(expression))
 73
 74    return self.func(
 75        "FROM_UNIXTIME",
 76        exp.Div(this=timestamp, expression=exp.func("POW", 10, scale)),
 77        self.format_time(expression),
 78    )
 79
 80
 81def date_add_sql(
 82    kind: str,
 83) -> t.Callable[[generator.Generator, exp.Expr], str]:
 84    def func(self: generator.Generator, expression: exp.Expr) -> str:
 85        return self.func(
 86            f"DATE_{kind}",
 87            expression.this,
 88            exp.Interval(this=expression.expression, unit=unit_to_var(expression)),
 89        )
 90
 91    return func
 92
 93
 94_MAKE_INTERVAL_UNIT_ALIASES = {
 95    "years": "year",
 96    "months": "month",
 97    "weeks": "week",
 98    "days": "day",
 99    "hours": "hour",
100    "minutes": "minute",
101    "mins": "minute",
102    "seconds": "second",
103    "secs": "second",
104}
105
106
107def _ts_or_ds_to_date_sql(self: MySQLGenerator, expression: exp.TsOrDsToDate) -> str:
108    time_format = expression.args.get("format")
109    return _str_to_date_sql(self, expression) if time_format else self.func("DATE", expression.this)
110
111
112class MySQLGenerator(generator.Generator):
113    BYTE_STRING_FUNCTION = "UNHEX"
114    SELECT_KINDS: tuple[str, ...] = ()
115    TRY_SUPPORTED = False
116    SUPPORTS_UESCAPE = False
117    SUPPORTS_DECODE_CASE = False
118    SUPPORTS_MODIFY_COLUMN = True
119    SUPPORTS_CHANGE_COLUMN = True
120    SUPPORTS_ALTER_COLUMN_NULLABILITY = True
121
122    AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
123
124    INTERVAL_ALLOWS_PLURAL_FORM = False
125    LOCKING_READS_SUPPORTED = True
126    CAST_TO_INT_ROUNDS = True
127    NULL_ORDERING_SUPPORTED: bool | None = None
128    JOIN_HINTS = False
129    TABLE_HINTS = True
130    DUPLICATE_KEY_UPDATE_WITH_SET = False
131    QUERY_HINT_SEP = " "
132    VALUES_AS_TABLE = False
133    NVL2_SUPPORTED = False
134    LAST_DAY_SUPPORTS_DATE_PART = False
135    JSON_TYPE_REQUIRED_FOR_EXTRACTION = True
136    JSON_PATH_BRACKETED_KEY_SUPPORTED = False
137    JSON_KEY_VALUE_PAIR_SEP = ","
138    SUPPORTS_TO_NUMBER = False
139    PARSE_JSON_NAME: str | None = None
140    PAD_FILL_PATTERN_IS_REQUIRED = True
141    WRAP_DERIVED_VALUES = False
142    VARCHAR_REQUIRES_SIZE = True
143    SUPPORTS_MEDIAN = False
144    UPDATE_STATEMENT_SUPPORTS_FROM = False
145
146    ALTER_PARTITION_OPTIONS: t.ClassVar[tuple[type[exp.Expr], ...]] = (
147        exp.PartitionByListProperty,
148        exp.PartitionByRangeProperty,
149    )
150
151    TRANSFORMS = {
152        **generator.Generator.TRANSFORMS,
153        exp.ArrayAgg: rename_func("GROUP_CONCAT"),
154        exp.BitwiseAndAgg: rename_func("BIT_AND"),
155        exp.BitwiseOrAgg: rename_func("BIT_OR"),
156        exp.BitwiseXorAgg: rename_func("BIT_XOR"),
157        exp.BitwiseCount: rename_func("BIT_COUNT"),
158        exp.Chr: lambda self, e: self.chr_sql(e, "CHAR"),
159        exp.CurrentDate: no_paren_current_date_sql,
160        exp.CurrentVersion: rename_func("VERSION"),
161        exp.DateDiff: remove_ts_or_ds_to_date(
162            lambda self, e: self.func("DATEDIFF", e.this, e.expression), ("this", "expression")
163        ),
164        exp.DateAdd: date_add_sql("ADD"),
165        exp.DateStrToDate: datestrtodate_sql,
166        exp.DateSub: date_add_sql("SUB"),
167        exp.DateTrunc: _date_trunc_sql,
168        exp.Day: remove_ts_or_ds_to_date(),
169        exp.DayOfMonth: remove_ts_or_ds_to_date(rename_func("DAYOFMONTH")),
170        exp.DayOfWeek: remove_ts_or_ds_to_date(rename_func("DAYOFWEEK")),
171        exp.DayOfYear: remove_ts_or_ds_to_date(rename_func("DAYOFYEAR")),
172        exp.GroupConcat: lambda self, e: (
173            f"""GROUP_CONCAT({self.sql(e, "this")} SEPARATOR {self.sql(e, "separator") or "','"})"""
174        ),
175        exp.ILike: no_ilike_sql,
176        # https://dev.mysql.com/doc/refman/9.7/en/flow-control-functions.html#function_if
177        exp.If: if_sql(false_value="NULL"),
178        exp.JSONExtractScalar: arrow_json_extract_sql,
179        exp.Length: length_or_char_length_sql,
180        exp.LogicalOr: rename_func("MAX"),
181        exp.LogicalAnd: rename_func("MIN"),
182        exp.Max: max_or_greatest,
183        exp.Min: min_or_least,
184        exp.Month: remove_ts_or_ds_to_date(),
185        exp.NullSafeEQ: lambda self, e: self.binary(e, "<=>"),
186        exp.NullSafeNEQ: lambda self, e: f"NOT {self.binary(e, '<=>')}",
187        exp.NumberToStr: rename_func("FORMAT"),
188        exp.Pivot: no_pivot_sql,
189        exp.Select: transforms.preprocess(
190            [
191                transforms.eliminate_distinct_on,
192                transforms.eliminate_semi_and_anti_joins,
193                transforms.eliminate_qualify,
194                transforms.eliminate_full_outer_join,
195                transforms.unnest_generate_date_array_using_recursive_cte,
196            ]
197        ),
198        exp.Stddev: rename_func("STDDEV_SAMP"),
199        exp.StrPosition: lambda self, e: strposition_sql(
200            self, e, func_name="LOCATE", supports_position=True
201        ),
202        exp.StrToDate: _str_to_date_sql,
203        exp.StrToTime: _str_to_date_sql,
204        exp.Stuff: rename_func("INSERT"),
205        exp.SessionUser: lambda *_: "SESSION_USER()",
206        exp.TableSample: no_tablesample_sql,
207        exp.TimeFromParts: rename_func("MAKETIME"),
208        exp.TimestampAdd: date_add_interval_sql("DATE", "ADD"),
209        exp.TimestampDiff: lambda self, e: self.func(
210            "TIMESTAMPDIFF", unit_to_var(e), e.expression, e.this
211        ),
212        exp.TimestampSub: date_add_interval_sql("DATE", "SUB"),
213        exp.TimeStrToUnix: rename_func("UNIX_TIMESTAMP"),
214        exp.TimeStrToTime: lambda self, e: timestrtotime_sql(
215            self,
216            e,
217            include_precision=not e.args.get("zone"),
218        ),
219        exp.TimeToStr: remove_ts_or_ds_to_date(
220            lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e))
221        ),
222        exp.Trim: trim_sql,
223        exp.Trunc: lambda self, e: self.func(
224            "TRUNCATE", e.this, e.args.get("decimals") or exp.Literal.number(0)
225        ),
226        exp.TryCast: no_trycast_sql,
227        exp.TsOrDsAdd: date_add_sql("ADD"),
228        exp.TsOrDsDiff: lambda self, e: self.func("DATEDIFF", e.this, e.expression),
229        exp.TsOrDsToDate: _ts_or_ds_to_date_sql,
230        exp.Unicode: lambda self, e: f"ORD(CONVERT({self.sql(e.this)} USING utf32))",
231        exp.UnixToTime: _unix_to_time_sql,
232        exp.Variance: rename_func("VAR_SAMP"),
233        exp.VariancePop: rename_func("VAR_POP"),
234        exp.Week: remove_ts_or_ds_to_date(),
235        exp.WeekOfYear: remove_ts_or_ds_to_date(rename_func("WEEKOFYEAR")),
236        exp.Year: remove_ts_or_ds_to_date(),
237        exp.UtcTimestamp: rename_func("UTC_TIMESTAMP"),
238        exp.UtcTime: rename_func("UTC_TIME"),
239    }
240
241    UNSIGNED_TYPE_MAPPING: t.ClassVar = {
242        exp.DType.UBIGINT: "BIGINT",
243        exp.DType.UINT: "INT",
244        exp.DType.UMEDIUMINT: "MEDIUMINT",
245        exp.DType.USMALLINT: "SMALLINT",
246        exp.DType.UTINYINT: "TINYINT",
247        exp.DType.UDECIMAL: "DECIMAL",
248        exp.DType.UDOUBLE: "DOUBLE",
249    }
250
251    TIMESTAMP_TYPE_MAPPING: t.ClassVar = {
252        exp.DType.DATETIME2: "DATETIME",
253        exp.DType.SMALLDATETIME: "DATETIME",
254        exp.DType.TIMESTAMP: "DATETIME",
255        exp.DType.TIMESTAMPNTZ: "DATETIME",
256        exp.DType.TIMESTAMPTZ: "TIMESTAMP",
257        exp.DType.TIMESTAMPLTZ: "TIMESTAMP",
258    }
259
260    TYPE_MAPPING: t.ClassVar = {
261        exp.DType.NCHAR: "CHAR",
262        exp.DType.NVARCHAR: "VARCHAR",
263        exp.DType.INET: "INET",
264        exp.DType.ROWVERSION: "VARBINARY",
265        exp.DType.UBIGINT: "BIGINT",
266        exp.DType.UINT: "INT",
267        exp.DType.UMEDIUMINT: "MEDIUMINT",
268        exp.DType.USMALLINT: "SMALLINT",
269        exp.DType.UTINYINT: "TINYINT",
270        exp.DType.UDECIMAL: "DECIMAL",
271        exp.DType.UDOUBLE: "DOUBLE",
272        exp.DType.DATETIME2: "DATETIME",
273        exp.DType.SMALLDATETIME: "DATETIME",
274        exp.DType.TIMESTAMP: "DATETIME",
275        exp.DType.TIMESTAMPNTZ: "DATETIME",
276        exp.DType.TIMESTAMPTZ: "TIMESTAMP",
277        exp.DType.TIMESTAMPLTZ: "TIMESTAMP",
278    }
279
280    PROPERTIES_LOCATION: t.ClassVar = {
281        **generator.Generator.PROPERTIES_LOCATION,
282        exp.TransientProperty: exp.Properties.Location.UNSUPPORTED,
283        exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED,
284        exp.PartitionedByProperty: exp.Properties.Location.UNSUPPORTED,
285        exp.PartitionByRangeProperty: exp.Properties.Location.POST_SCHEMA,
286        exp.PartitionByListProperty: exp.Properties.Location.POST_SCHEMA,
287        exp.Property: exp.Properties.Location.POST_SCHEMA,
288    }
289
290    LIMIT_FETCH = "LIMIT"
291
292    LIMIT_ONLY_LITERALS = True
293
294    CHAR_CAST_MAPPING: t.ClassVar = dict.fromkeys(
295        (
296            exp.DType.LONGTEXT,
297            exp.DType.LONGBLOB,
298            exp.DType.MEDIUMBLOB,
299            exp.DType.MEDIUMTEXT,
300            exp.DType.TEXT,
301            exp.DType.TINYBLOB,
302            exp.DType.TINYTEXT,
303            exp.DType.VARCHAR,
304        ),
305        "CHAR",
306    )
307    SIGNED_CAST_MAPPING: t.ClassVar = dict.fromkeys(
308        (
309            exp.DType.BIGINT,
310            exp.DType.BOOLEAN,
311            exp.DType.INT,
312            exp.DType.SMALLINT,
313            exp.DType.TINYINT,
314            exp.DType.MEDIUMINT,
315        ),
316        "SIGNED",
317    )
318
319    # MySQL doesn't support many datatypes in cast.
320    # https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html#function_cast
321    CAST_MAPPING: t.ClassVar = {
322        exp.DType.LONGTEXT: "CHAR",
323        exp.DType.LONGBLOB: "CHAR",
324        exp.DType.MEDIUMBLOB: "CHAR",
325        exp.DType.MEDIUMTEXT: "CHAR",
326        exp.DType.TEXT: "CHAR",
327        exp.DType.TINYBLOB: "CHAR",
328        exp.DType.TINYTEXT: "CHAR",
329        exp.DType.VARCHAR: "CHAR",
330        exp.DType.BIGINT: "SIGNED",
331        exp.DType.BOOLEAN: "SIGNED",
332        exp.DType.INT: "SIGNED",
333        exp.DType.SMALLINT: "SIGNED",
334        exp.DType.TINYINT: "SIGNED",
335        exp.DType.MEDIUMINT: "SIGNED",
336        exp.DType.UBIGINT: "UNSIGNED",
337    }
338
339    TIMESTAMP_FUNC_TYPES: t.ClassVar = {
340        exp.DType.TIMESTAMPTZ,
341        exp.DType.TIMESTAMPLTZ,
342    }
343
344    # https://dev.mysql.com/doc/refman/8.0/en/keywords.html
345    RESERVED_KEYWORDS = {
346        "accessible",
347        "add",
348        "all",
349        "alter",
350        "analyze",
351        "and",
352        "as",
353        "asc",
354        "asensitive",
355        "before",
356        "between",
357        "bigint",
358        "binary",
359        "blob",
360        "both",
361        "by",
362        "call",
363        "cascade",
364        "case",
365        "change",
366        "char",
367        "character",
368        "check",
369        "collate",
370        "column",
371        "condition",
372        "constraint",
373        "continue",
374        "convert",
375        "create",
376        "cross",
377        "cube",
378        "cume_dist",
379        "current_date",
380        "current_time",
381        "current_timestamp",
382        "current_user",
383        "cursor",
384        "database",
385        "databases",
386        "day_hour",
387        "day_microsecond",
388        "day_minute",
389        "day_second",
390        "dec",
391        "decimal",
392        "declare",
393        "default",
394        "delayed",
395        "delete",
396        "dense_rank",
397        "desc",
398        "describe",
399        "deterministic",
400        "distinct",
401        "distinctrow",
402        "div",
403        "double",
404        "drop",
405        "dual",
406        "each",
407        "else",
408        "elseif",
409        "empty",
410        "enclosed",
411        "escaped",
412        "except",
413        "exists",
414        "exit",
415        "explain",
416        "false",
417        "fetch",
418        "first_value",
419        "float",
420        "float4",
421        "float8",
422        "for",
423        "force",
424        "foreign",
425        "from",
426        "fulltext",
427        "function",
428        "generated",
429        "get",
430        "grant",
431        "group",
432        "grouping",
433        "groups",
434        "having",
435        "high_priority",
436        "hour_microsecond",
437        "hour_minute",
438        "hour_second",
439        "if",
440        "ignore",
441        "in",
442        "index",
443        "infile",
444        "inner",
445        "inout",
446        "insensitive",
447        "insert",
448        "int",
449        "int1",
450        "int2",
451        "int3",
452        "int4",
453        "int8",
454        "integer",
455        "intersect",
456        "interval",
457        "into",
458        "io_after_gtids",
459        "io_before_gtids",
460        "is",
461        "iterate",
462        "join",
463        "json_table",
464        "key",
465        "keys",
466        "kill",
467        "lag",
468        "last_value",
469        "lateral",
470        "lead",
471        "leading",
472        "leave",
473        "left",
474        "like",
475        "limit",
476        "linear",
477        "lines",
478        "load",
479        "localtime",
480        "localtimestamp",
481        "lock",
482        "long",
483        "longblob",
484        "longtext",
485        "loop",
486        "low_priority",
487        "master_bind",
488        "master_ssl_verify_server_cert",
489        "match",
490        "maxvalue",
491        "mediumblob",
492        "mediumint",
493        "mediumtext",
494        "middleint",
495        "minute_microsecond",
496        "minute_second",
497        "mod",
498        "modifies",
499        "natural",
500        "not",
501        "no_write_to_binlog",
502        "nth_value",
503        "ntile",
504        "null",
505        "numeric",
506        "of",
507        "on",
508        "optimize",
509        "optimizer_costs",
510        "option",
511        "optionally",
512        "or",
513        "order",
514        "out",
515        "outer",
516        "outfile",
517        "over",
518        "partition",
519        "percent_rank",
520        "precision",
521        "primary",
522        "procedure",
523        "purge",
524        "range",
525        "rank",
526        "read",
527        "reads",
528        "read_write",
529        "real",
530        "recursive",
531        "references",
532        "regexp",
533        "release",
534        "rename",
535        "repeat",
536        "replace",
537        "require",
538        "resignal",
539        "restrict",
540        "return",
541        "revoke",
542        "right",
543        "rlike",
544        "row",
545        "rows",
546        "row_number",
547        "schema",
548        "schemas",
549        "second_microsecond",
550        "select",
551        "sensitive",
552        "separator",
553        "set",
554        "show",
555        "signal",
556        "smallint",
557        "spatial",
558        "specific",
559        "sql",
560        "sqlexception",
561        "sqlstate",
562        "sqlwarning",
563        "sql_big_result",
564        "sql_calc_found_rows",
565        "sql_small_result",
566        "ssl",
567        "starting",
568        "stored",
569        "straight_join",
570        "system",
571        "table",
572        "terminated",
573        "then",
574        "tinyblob",
575        "tinyint",
576        "tinytext",
577        "to",
578        "trailing",
579        "trigger",
580        "true",
581        "undo",
582        "union",
583        "unique",
584        "unlock",
585        "unsigned",
586        "update",
587        "usage",
588        "use",
589        "using",
590        "utc_date",
591        "utc_time",
592        "utc_timestamp",
593        "values",
594        "varbinary",
595        "varchar",
596        "varcharacter",
597        "varying",
598        "virtual",
599        "when",
600        "where",
601        "while",
602        "window",
603        "with",
604        "write",
605        "xor",
606        "year_month",
607        "zerofill",
608    }
609
610    SQL_SECURITY_VIEW_LOCATION: t.ClassVar = exp.Properties.Location.POST_CREATE
611
612    def makeinterval_sql(self: MySQLGenerator, expression: exp.MakeInterval) -> str:
613        intervals: list[exp.Interval] = []
614        for arg_key, value in expression.args.items():
615            if value is None:
616                continue
617
618            if isinstance(value, exp.Kwarg):
619                unit_name = _MAKE_INTERVAL_UNIT_ALIASES.get(
620                    value.this.name.lower(), value.this.name.lower()
621                )
622                value = value.expression
623            else:
624                unit_name = arg_key
625
626            intervals.append(exp.Interval(this=value.copy(), unit=exp.var(unit_name.upper())))
627
628        if not intervals:
629            return self.function_fallback_sql(expression)
630
631        parent = expression.parent
632        sep = " - " if isinstance(parent, exp.Sub) and parent.expression is expression else " + "
633
634        return sep.join(self.sql(interval) for interval in intervals)
635
636    def locate_properties(self, properties: exp.Properties) -> defaultdict:
637        locations = super().locate_properties(properties)
638
639        # MySQL puts SQL SECURITY before VIEW but after the schema for functions/procedures
640        if isinstance(create := properties.parent, exp.Create) and create.kind == "VIEW":
641            post_schema = locations[exp.Properties.Location.POST_SCHEMA]
642            for i, p in enumerate(post_schema):
643                if isinstance(p, exp.SqlSecurityProperty):
644                    post_schema.pop(i)
645                    locations[self.SQL_SECURITY_VIEW_LOCATION].append(p)
646                    break
647
648        return locations
649
650    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
651        persisted = "STORED" if expression.args.get("persisted") else "VIRTUAL"
652        return f"GENERATED ALWAYS AS ({self.sql(expression.this.unnest())}) {persisted}"
653
654    def array_sql(self, expression: exp.Array) -> str:
655        self.unsupported("Arrays are not supported by MySQL")
656        return self.function_fallback_sql(expression)
657
658    def arraycontainsall_sql(self, expression: exp.ArrayContainsAll) -> str:
659        self.unsupported("Array operations are not supported by MySQL")
660        return self.function_fallback_sql(expression)
661
662    def arraycontainedby_sql(self, expression: exp.ArrayContainedBy) -> str:
663        self.unsupported("Array operations are not supported by MySQL")
664        return self.function_fallback_sql(expression)
665
666    def dpipe_sql(self, expression: exp.DPipe) -> str:
667        return self.func("CONCAT", *expression.flatten())
668
669    def intdiv_sql(self, expression: exp.IntDiv) -> str:
670        this = self.sql(exp._wrap(expression.this, exp.Binary))
671        divisor = self.sql(exp._wrap(expression.expression, exp.Binary))
672        sql = f"{this} {self.maybe_comment('DIV', comments=expression.comments)} {divisor}"
673
674        parent = expression.parent
675        if isinstance(parent, (exp.Neg, exp.BitwiseNot)) or (
676            isinstance(parent, (exp.Mul, exp.Div, exp.IntDiv, exp.Mod))
677            and parent.expression is expression
678        ):
679            return f"({sql})"
680
681        return sql
682
683    def extract_sql(self, expression: exp.Extract) -> str:
684        unit = expression.name
685        if unit and unit.lower() == "epoch":
686            return self.func("UNIX_TIMESTAMP", expression.expression)
687
688        return super().extract_sql(expression)
689
690    def datatype_sql(self, expression: exp.DataType) -> str:
691        if (
692            self.VARCHAR_REQUIRES_SIZE
693            and expression.is_type(exp.DType.VARCHAR)
694            and not expression.expressions
695        ):
696            # `VARCHAR` must always have a size - if it doesn't, we always generate `TEXT`
697            return "TEXT"
698
699        # https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
700        result = super().datatype_sql(expression)
701        if expression.this in self.UNSIGNED_TYPE_MAPPING:
702            result = f"{result} UNSIGNED"
703
704        return result
705
706    def jsonarraycontains_sql(self, expression: exp.JSONArrayContains) -> str:
707        return f"{self.sql(expression, 'this')} MEMBER OF({self.sql(expression, 'expression')})"
708
709    def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str:
710        if expression.to.this in self.TIMESTAMP_FUNC_TYPES:
711            return self.func("TIMESTAMP", expression.this)
712
713        to = self.CAST_MAPPING.get(expression.to.this)
714
715        if to:
716            expression.to.set("this", to)
717        return super().cast_sql(expression)
718
719    def show_sql(self, expression: exp.Show) -> str:
720        this = f" {expression.name}"
721        full = " FULL" if expression.args.get("full") else ""
722        global_ = " GLOBAL" if expression.args.get("global_") else ""
723
724        target = self.sql(expression, "target")
725
726        # SHOW CREATE ... takes a possibly qualified name, it has no FROM clause
727        # https://dev.mysql.com/doc/refman/8.4/en/show-create-table.html
728        if expression.name.startswith("CREATE") and (db_id := self.sql(expression, "db")):
729            target = f"{db_id}.{target}"
730            db = ""
731        else:
732            db = self._prefixed_sql("FROM", expression, "db")
733
734        target = f" {target}" if target else ""
735        if expression.name in ("COLUMNS", "INDEX"):
736            target = f" FROM{target}"
737        elif expression.name == "GRANTS":
738            target = f" FOR{target}"
739        elif expression.name in ("LINKS", "PARTITIONS"):
740            target = f" ON{target}" if target else ""
741        elif expression.name == "PROJECTIONS":
742            target = f" ON TABLE{target}" if target else ""
743
744        like = self._prefixed_sql("LIKE", expression, "like")
745        where = self.sql(expression, "where")
746
747        types = self.expressions(expression, key="types")
748        types = f" {types}" if types else types
749        query = self._prefixed_sql("FOR QUERY", expression, "query")
750
751        if expression.name == "PROFILE":
752            offset = self._prefixed_sql("OFFSET", expression, "offset")
753            limit = self._prefixed_sql("LIMIT", expression, "limit")
754        else:
755            offset = ""
756            limit = self._oldstyle_limit_sql(expression)
757
758        log = self._prefixed_sql("IN", expression, "log")
759        position = self._prefixed_sql("FROM", expression, "position")
760
761        channel = self._prefixed_sql("FOR CHANNEL", expression, "channel")
762
763        if expression.name == "ENGINE":
764            mutex_or_status = " MUTEX" if expression.args.get("mutex") else " STATUS"
765        else:
766            mutex_or_status = ""
767
768        for_table = self._prefixed_sql("FOR TABLE", expression, "for_table")
769        for_group = self._prefixed_sql("FOR GROUP", expression, "for_group")
770        for_user = self._prefixed_sql("FOR USER", expression, "for_user")
771        for_role = self._prefixed_sql("FOR ROLE", expression, "for_role")
772        into_outfile = self._prefixed_sql("INTO OUTFILE", expression, "into_outfile")
773        json = " JSON" if expression.args.get("json") else ""
774
775        return f"SHOW{full}{global_}{this}{json}{target}{for_table}{types}{db}{query}{log}{position}{channel}{mutex_or_status}{like}{where}{offset}{limit}{for_group}{for_user}{for_role}{into_outfile}"
776
777    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
778        """To avoid TO keyword in ALTER ... RENAME statements.
779        It's moved from Doris, because it's the same for all MySQL, Doris, and StarRocks.
780        """
781        return super().alterrename_sql(expression, include_to=False)
782
783    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
784        dtype = self.sql(expression, "dtype")
785        if not dtype:
786            return super().altercolumn_sql(expression)
787
788        if expression.args.get("exists"):
789            self.unsupported("ALTER COLUMN IF EXISTS is not supported by this dialect")
790
791        this = self.sql(expression, "this")
792        null_constraint = self._alter_column_null_constraint_sql(expression)
793        return f"MODIFY COLUMN {this} {dtype}{null_constraint}"
794
795    def _prefixed_sql(self, prefix: str, expression: exp.Expr, arg: str) -> str:
796        sql = self.sql(expression, arg)
797        return f" {prefix} {sql}" if sql else ""
798
799    def _oldstyle_limit_sql(self, expression: exp.Show) -> str:
800        limit = self.sql(expression, "limit")
801        offset = self.sql(expression, "offset")
802        if limit:
803            limit_offset = f"{offset}, {limit}" if offset else limit
804            return f" LIMIT {limit_offset}"
805        return ""
806
807    def timestamptrunc_sql(self, expression: exp.TimestampTrunc) -> str:
808        unit = expression.args.get("unit")
809        if isinstance(unit, exp.WeekStart):
810            unit = exp.var(self.weekstart_name(unit))
811
812        # Pick an old-enough date to avoid negative timestamp diffs
813        start_ts = "'0000-01-01 00:00:00'"
814
815        # Source: https://stackoverflow.com/a/32955740
816        timestamp_diff = build_date_delta(exp.TimestampDiff)([unit, start_ts, expression.this])
817        interval = exp.Interval(this=timestamp_diff, unit=unit)
818        dateadd = build_date_delta_with_interval(exp.DateAdd)([start_ts, interval])
819
820        return self.sql(dateadd)
821
822    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
823        from_tz = expression.args.get("source_tz")
824        to_tz = expression.args.get("target_tz")
825        dt = expression.args.get("timestamp")
826
827        return self.func("CONVERT_TZ", dt, from_tz, to_tz)
828
829    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
830        self.unsupported("AT TIME ZONE is not supported by MySQL")
831        return self.sql(expression.this)
832
833    def isascii_sql(self, expression: exp.IsAscii) -> str:
834        return f"REGEXP_LIKE({self.sql(expression.this)}, '^[[:ascii:]]*$')"
835
836    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
837        # https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html
838        self.unsupported("MySQL does not support IGNORE NULLS.")
839        return self.sql(expression.this)
840
841    @unsupported_args("this")
842    def currentschema_sql(self, expression: exp.CurrentSchema) -> str:
843        return self.func("SCHEMA")
844
845    def partition_sql(self, expression: exp.Partition) -> str:
846        parent = expression.parent
847        if isinstance(parent, (exp.PartitionByRangeProperty, exp.PartitionByListProperty)):
848            return self.expressions(expression, flat=True)
849        return super().partition_sql(expression)
850
851    def _partition_by_sql(
852        self, expression: exp.PartitionByRangeProperty | exp.PartitionByListProperty, kind: str
853    ) -> str:
854        partitions = self.expressions(expression, key="partition_expressions", flat=True)
855        create = self.expressions(expression, key="create_expressions", flat=True)
856        return f"PARTITION BY {kind} ({partitions}) ({create})"
857
858    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
859        return self._partition_by_sql(expression, "RANGE")
860
861    def partitionbylistproperty_sql(self, expression: exp.PartitionByListProperty) -> str:
862        return self._partition_by_sql(expression, "LIST")
863
864    def partitionlist_sql(self, expression: exp.PartitionList) -> str:
865        name = self.sql(expression, "this")
866        values = self.expressions(expression, flat=True)
867        return f"PARTITION {name} VALUES IN ({values})"
868
869    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
870        name = self.sql(expression, "this")
871        values = self.expressions(expression, flat=True)
872        return f"PARTITION {name} VALUES LESS THAN ({values})"
def date_add_sql( kind: str) -> Callable[[sqlglot.generator.Generator, sqlglot.expressions.core.Expr], str]:
82def date_add_sql(
83    kind: str,
84) -> t.Callable[[generator.Generator, exp.Expr], str]:
85    def func(self: generator.Generator, expression: exp.Expr) -> str:
86        return self.func(
87            f"DATE_{kind}",
88            expression.this,
89            exp.Interval(this=expression.expression, unit=unit_to_var(expression)),
90        )
91
92    return func
class MySQLGenerator(sqlglot.generator.Generator):
113class MySQLGenerator(generator.Generator):
114    BYTE_STRING_FUNCTION = "UNHEX"
115    SELECT_KINDS: tuple[str, ...] = ()
116    TRY_SUPPORTED = False
117    SUPPORTS_UESCAPE = False
118    SUPPORTS_DECODE_CASE = False
119    SUPPORTS_MODIFY_COLUMN = True
120    SUPPORTS_CHANGE_COLUMN = True
121    SUPPORTS_ALTER_COLUMN_NULLABILITY = True
122
123    AFTER_HAVING_MODIFIER_TRANSFORMS = generator.AFTER_HAVING_MODIFIER_TRANSFORMS
124
125    INTERVAL_ALLOWS_PLURAL_FORM = False
126    LOCKING_READS_SUPPORTED = True
127    CAST_TO_INT_ROUNDS = True
128    NULL_ORDERING_SUPPORTED: bool | None = None
129    JOIN_HINTS = False
130    TABLE_HINTS = True
131    DUPLICATE_KEY_UPDATE_WITH_SET = False
132    QUERY_HINT_SEP = " "
133    VALUES_AS_TABLE = False
134    NVL2_SUPPORTED = False
135    LAST_DAY_SUPPORTS_DATE_PART = False
136    JSON_TYPE_REQUIRED_FOR_EXTRACTION = True
137    JSON_PATH_BRACKETED_KEY_SUPPORTED = False
138    JSON_KEY_VALUE_PAIR_SEP = ","
139    SUPPORTS_TO_NUMBER = False
140    PARSE_JSON_NAME: str | None = None
141    PAD_FILL_PATTERN_IS_REQUIRED = True
142    WRAP_DERIVED_VALUES = False
143    VARCHAR_REQUIRES_SIZE = True
144    SUPPORTS_MEDIAN = False
145    UPDATE_STATEMENT_SUPPORTS_FROM = False
146
147    ALTER_PARTITION_OPTIONS: t.ClassVar[tuple[type[exp.Expr], ...]] = (
148        exp.PartitionByListProperty,
149        exp.PartitionByRangeProperty,
150    )
151
152    TRANSFORMS = {
153        **generator.Generator.TRANSFORMS,
154        exp.ArrayAgg: rename_func("GROUP_CONCAT"),
155        exp.BitwiseAndAgg: rename_func("BIT_AND"),
156        exp.BitwiseOrAgg: rename_func("BIT_OR"),
157        exp.BitwiseXorAgg: rename_func("BIT_XOR"),
158        exp.BitwiseCount: rename_func("BIT_COUNT"),
159        exp.Chr: lambda self, e: self.chr_sql(e, "CHAR"),
160        exp.CurrentDate: no_paren_current_date_sql,
161        exp.CurrentVersion: rename_func("VERSION"),
162        exp.DateDiff: remove_ts_or_ds_to_date(
163            lambda self, e: self.func("DATEDIFF", e.this, e.expression), ("this", "expression")
164        ),
165        exp.DateAdd: date_add_sql("ADD"),
166        exp.DateStrToDate: datestrtodate_sql,
167        exp.DateSub: date_add_sql("SUB"),
168        exp.DateTrunc: _date_trunc_sql,
169        exp.Day: remove_ts_or_ds_to_date(),
170        exp.DayOfMonth: remove_ts_or_ds_to_date(rename_func("DAYOFMONTH")),
171        exp.DayOfWeek: remove_ts_or_ds_to_date(rename_func("DAYOFWEEK")),
172        exp.DayOfYear: remove_ts_or_ds_to_date(rename_func("DAYOFYEAR")),
173        exp.GroupConcat: lambda self, e: (
174            f"""GROUP_CONCAT({self.sql(e, "this")} SEPARATOR {self.sql(e, "separator") or "','"})"""
175        ),
176        exp.ILike: no_ilike_sql,
177        # https://dev.mysql.com/doc/refman/9.7/en/flow-control-functions.html#function_if
178        exp.If: if_sql(false_value="NULL"),
179        exp.JSONExtractScalar: arrow_json_extract_sql,
180        exp.Length: length_or_char_length_sql,
181        exp.LogicalOr: rename_func("MAX"),
182        exp.LogicalAnd: rename_func("MIN"),
183        exp.Max: max_or_greatest,
184        exp.Min: min_or_least,
185        exp.Month: remove_ts_or_ds_to_date(),
186        exp.NullSafeEQ: lambda self, e: self.binary(e, "<=>"),
187        exp.NullSafeNEQ: lambda self, e: f"NOT {self.binary(e, '<=>')}",
188        exp.NumberToStr: rename_func("FORMAT"),
189        exp.Pivot: no_pivot_sql,
190        exp.Select: transforms.preprocess(
191            [
192                transforms.eliminate_distinct_on,
193                transforms.eliminate_semi_and_anti_joins,
194                transforms.eliminate_qualify,
195                transforms.eliminate_full_outer_join,
196                transforms.unnest_generate_date_array_using_recursive_cte,
197            ]
198        ),
199        exp.Stddev: rename_func("STDDEV_SAMP"),
200        exp.StrPosition: lambda self, e: strposition_sql(
201            self, e, func_name="LOCATE", supports_position=True
202        ),
203        exp.StrToDate: _str_to_date_sql,
204        exp.StrToTime: _str_to_date_sql,
205        exp.Stuff: rename_func("INSERT"),
206        exp.SessionUser: lambda *_: "SESSION_USER()",
207        exp.TableSample: no_tablesample_sql,
208        exp.TimeFromParts: rename_func("MAKETIME"),
209        exp.TimestampAdd: date_add_interval_sql("DATE", "ADD"),
210        exp.TimestampDiff: lambda self, e: self.func(
211            "TIMESTAMPDIFF", unit_to_var(e), e.expression, e.this
212        ),
213        exp.TimestampSub: date_add_interval_sql("DATE", "SUB"),
214        exp.TimeStrToUnix: rename_func("UNIX_TIMESTAMP"),
215        exp.TimeStrToTime: lambda self, e: timestrtotime_sql(
216            self,
217            e,
218            include_precision=not e.args.get("zone"),
219        ),
220        exp.TimeToStr: remove_ts_or_ds_to_date(
221            lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e))
222        ),
223        exp.Trim: trim_sql,
224        exp.Trunc: lambda self, e: self.func(
225            "TRUNCATE", e.this, e.args.get("decimals") or exp.Literal.number(0)
226        ),
227        exp.TryCast: no_trycast_sql,
228        exp.TsOrDsAdd: date_add_sql("ADD"),
229        exp.TsOrDsDiff: lambda self, e: self.func("DATEDIFF", e.this, e.expression),
230        exp.TsOrDsToDate: _ts_or_ds_to_date_sql,
231        exp.Unicode: lambda self, e: f"ORD(CONVERT({self.sql(e.this)} USING utf32))",
232        exp.UnixToTime: _unix_to_time_sql,
233        exp.Variance: rename_func("VAR_SAMP"),
234        exp.VariancePop: rename_func("VAR_POP"),
235        exp.Week: remove_ts_or_ds_to_date(),
236        exp.WeekOfYear: remove_ts_or_ds_to_date(rename_func("WEEKOFYEAR")),
237        exp.Year: remove_ts_or_ds_to_date(),
238        exp.UtcTimestamp: rename_func("UTC_TIMESTAMP"),
239        exp.UtcTime: rename_func("UTC_TIME"),
240    }
241
242    UNSIGNED_TYPE_MAPPING: t.ClassVar = {
243        exp.DType.UBIGINT: "BIGINT",
244        exp.DType.UINT: "INT",
245        exp.DType.UMEDIUMINT: "MEDIUMINT",
246        exp.DType.USMALLINT: "SMALLINT",
247        exp.DType.UTINYINT: "TINYINT",
248        exp.DType.UDECIMAL: "DECIMAL",
249        exp.DType.UDOUBLE: "DOUBLE",
250    }
251
252    TIMESTAMP_TYPE_MAPPING: t.ClassVar = {
253        exp.DType.DATETIME2: "DATETIME",
254        exp.DType.SMALLDATETIME: "DATETIME",
255        exp.DType.TIMESTAMP: "DATETIME",
256        exp.DType.TIMESTAMPNTZ: "DATETIME",
257        exp.DType.TIMESTAMPTZ: "TIMESTAMP",
258        exp.DType.TIMESTAMPLTZ: "TIMESTAMP",
259    }
260
261    TYPE_MAPPING: t.ClassVar = {
262        exp.DType.NCHAR: "CHAR",
263        exp.DType.NVARCHAR: "VARCHAR",
264        exp.DType.INET: "INET",
265        exp.DType.ROWVERSION: "VARBINARY",
266        exp.DType.UBIGINT: "BIGINT",
267        exp.DType.UINT: "INT",
268        exp.DType.UMEDIUMINT: "MEDIUMINT",
269        exp.DType.USMALLINT: "SMALLINT",
270        exp.DType.UTINYINT: "TINYINT",
271        exp.DType.UDECIMAL: "DECIMAL",
272        exp.DType.UDOUBLE: "DOUBLE",
273        exp.DType.DATETIME2: "DATETIME",
274        exp.DType.SMALLDATETIME: "DATETIME",
275        exp.DType.TIMESTAMP: "DATETIME",
276        exp.DType.TIMESTAMPNTZ: "DATETIME",
277        exp.DType.TIMESTAMPTZ: "TIMESTAMP",
278        exp.DType.TIMESTAMPLTZ: "TIMESTAMP",
279    }
280
281    PROPERTIES_LOCATION: t.ClassVar = {
282        **generator.Generator.PROPERTIES_LOCATION,
283        exp.TransientProperty: exp.Properties.Location.UNSUPPORTED,
284        exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED,
285        exp.PartitionedByProperty: exp.Properties.Location.UNSUPPORTED,
286        exp.PartitionByRangeProperty: exp.Properties.Location.POST_SCHEMA,
287        exp.PartitionByListProperty: exp.Properties.Location.POST_SCHEMA,
288        exp.Property: exp.Properties.Location.POST_SCHEMA,
289    }
290
291    LIMIT_FETCH = "LIMIT"
292
293    LIMIT_ONLY_LITERALS = True
294
295    CHAR_CAST_MAPPING: t.ClassVar = dict.fromkeys(
296        (
297            exp.DType.LONGTEXT,
298            exp.DType.LONGBLOB,
299            exp.DType.MEDIUMBLOB,
300            exp.DType.MEDIUMTEXT,
301            exp.DType.TEXT,
302            exp.DType.TINYBLOB,
303            exp.DType.TINYTEXT,
304            exp.DType.VARCHAR,
305        ),
306        "CHAR",
307    )
308    SIGNED_CAST_MAPPING: t.ClassVar = dict.fromkeys(
309        (
310            exp.DType.BIGINT,
311            exp.DType.BOOLEAN,
312            exp.DType.INT,
313            exp.DType.SMALLINT,
314            exp.DType.TINYINT,
315            exp.DType.MEDIUMINT,
316        ),
317        "SIGNED",
318    )
319
320    # MySQL doesn't support many datatypes in cast.
321    # https://dev.mysql.com/doc/refman/8.0/en/cast-functions.html#function_cast
322    CAST_MAPPING: t.ClassVar = {
323        exp.DType.LONGTEXT: "CHAR",
324        exp.DType.LONGBLOB: "CHAR",
325        exp.DType.MEDIUMBLOB: "CHAR",
326        exp.DType.MEDIUMTEXT: "CHAR",
327        exp.DType.TEXT: "CHAR",
328        exp.DType.TINYBLOB: "CHAR",
329        exp.DType.TINYTEXT: "CHAR",
330        exp.DType.VARCHAR: "CHAR",
331        exp.DType.BIGINT: "SIGNED",
332        exp.DType.BOOLEAN: "SIGNED",
333        exp.DType.INT: "SIGNED",
334        exp.DType.SMALLINT: "SIGNED",
335        exp.DType.TINYINT: "SIGNED",
336        exp.DType.MEDIUMINT: "SIGNED",
337        exp.DType.UBIGINT: "UNSIGNED",
338    }
339
340    TIMESTAMP_FUNC_TYPES: t.ClassVar = {
341        exp.DType.TIMESTAMPTZ,
342        exp.DType.TIMESTAMPLTZ,
343    }
344
345    # https://dev.mysql.com/doc/refman/8.0/en/keywords.html
346    RESERVED_KEYWORDS = {
347        "accessible",
348        "add",
349        "all",
350        "alter",
351        "analyze",
352        "and",
353        "as",
354        "asc",
355        "asensitive",
356        "before",
357        "between",
358        "bigint",
359        "binary",
360        "blob",
361        "both",
362        "by",
363        "call",
364        "cascade",
365        "case",
366        "change",
367        "char",
368        "character",
369        "check",
370        "collate",
371        "column",
372        "condition",
373        "constraint",
374        "continue",
375        "convert",
376        "create",
377        "cross",
378        "cube",
379        "cume_dist",
380        "current_date",
381        "current_time",
382        "current_timestamp",
383        "current_user",
384        "cursor",
385        "database",
386        "databases",
387        "day_hour",
388        "day_microsecond",
389        "day_minute",
390        "day_second",
391        "dec",
392        "decimal",
393        "declare",
394        "default",
395        "delayed",
396        "delete",
397        "dense_rank",
398        "desc",
399        "describe",
400        "deterministic",
401        "distinct",
402        "distinctrow",
403        "div",
404        "double",
405        "drop",
406        "dual",
407        "each",
408        "else",
409        "elseif",
410        "empty",
411        "enclosed",
412        "escaped",
413        "except",
414        "exists",
415        "exit",
416        "explain",
417        "false",
418        "fetch",
419        "first_value",
420        "float",
421        "float4",
422        "float8",
423        "for",
424        "force",
425        "foreign",
426        "from",
427        "fulltext",
428        "function",
429        "generated",
430        "get",
431        "grant",
432        "group",
433        "grouping",
434        "groups",
435        "having",
436        "high_priority",
437        "hour_microsecond",
438        "hour_minute",
439        "hour_second",
440        "if",
441        "ignore",
442        "in",
443        "index",
444        "infile",
445        "inner",
446        "inout",
447        "insensitive",
448        "insert",
449        "int",
450        "int1",
451        "int2",
452        "int3",
453        "int4",
454        "int8",
455        "integer",
456        "intersect",
457        "interval",
458        "into",
459        "io_after_gtids",
460        "io_before_gtids",
461        "is",
462        "iterate",
463        "join",
464        "json_table",
465        "key",
466        "keys",
467        "kill",
468        "lag",
469        "last_value",
470        "lateral",
471        "lead",
472        "leading",
473        "leave",
474        "left",
475        "like",
476        "limit",
477        "linear",
478        "lines",
479        "load",
480        "localtime",
481        "localtimestamp",
482        "lock",
483        "long",
484        "longblob",
485        "longtext",
486        "loop",
487        "low_priority",
488        "master_bind",
489        "master_ssl_verify_server_cert",
490        "match",
491        "maxvalue",
492        "mediumblob",
493        "mediumint",
494        "mediumtext",
495        "middleint",
496        "minute_microsecond",
497        "minute_second",
498        "mod",
499        "modifies",
500        "natural",
501        "not",
502        "no_write_to_binlog",
503        "nth_value",
504        "ntile",
505        "null",
506        "numeric",
507        "of",
508        "on",
509        "optimize",
510        "optimizer_costs",
511        "option",
512        "optionally",
513        "or",
514        "order",
515        "out",
516        "outer",
517        "outfile",
518        "over",
519        "partition",
520        "percent_rank",
521        "precision",
522        "primary",
523        "procedure",
524        "purge",
525        "range",
526        "rank",
527        "read",
528        "reads",
529        "read_write",
530        "real",
531        "recursive",
532        "references",
533        "regexp",
534        "release",
535        "rename",
536        "repeat",
537        "replace",
538        "require",
539        "resignal",
540        "restrict",
541        "return",
542        "revoke",
543        "right",
544        "rlike",
545        "row",
546        "rows",
547        "row_number",
548        "schema",
549        "schemas",
550        "second_microsecond",
551        "select",
552        "sensitive",
553        "separator",
554        "set",
555        "show",
556        "signal",
557        "smallint",
558        "spatial",
559        "specific",
560        "sql",
561        "sqlexception",
562        "sqlstate",
563        "sqlwarning",
564        "sql_big_result",
565        "sql_calc_found_rows",
566        "sql_small_result",
567        "ssl",
568        "starting",
569        "stored",
570        "straight_join",
571        "system",
572        "table",
573        "terminated",
574        "then",
575        "tinyblob",
576        "tinyint",
577        "tinytext",
578        "to",
579        "trailing",
580        "trigger",
581        "true",
582        "undo",
583        "union",
584        "unique",
585        "unlock",
586        "unsigned",
587        "update",
588        "usage",
589        "use",
590        "using",
591        "utc_date",
592        "utc_time",
593        "utc_timestamp",
594        "values",
595        "varbinary",
596        "varchar",
597        "varcharacter",
598        "varying",
599        "virtual",
600        "when",
601        "where",
602        "while",
603        "window",
604        "with",
605        "write",
606        "xor",
607        "year_month",
608        "zerofill",
609    }
610
611    SQL_SECURITY_VIEW_LOCATION: t.ClassVar = exp.Properties.Location.POST_CREATE
612
613    def makeinterval_sql(self: MySQLGenerator, expression: exp.MakeInterval) -> str:
614        intervals: list[exp.Interval] = []
615        for arg_key, value in expression.args.items():
616            if value is None:
617                continue
618
619            if isinstance(value, exp.Kwarg):
620                unit_name = _MAKE_INTERVAL_UNIT_ALIASES.get(
621                    value.this.name.lower(), value.this.name.lower()
622                )
623                value = value.expression
624            else:
625                unit_name = arg_key
626
627            intervals.append(exp.Interval(this=value.copy(), unit=exp.var(unit_name.upper())))
628
629        if not intervals:
630            return self.function_fallback_sql(expression)
631
632        parent = expression.parent
633        sep = " - " if isinstance(parent, exp.Sub) and parent.expression is expression else " + "
634
635        return sep.join(self.sql(interval) for interval in intervals)
636
637    def locate_properties(self, properties: exp.Properties) -> defaultdict:
638        locations = super().locate_properties(properties)
639
640        # MySQL puts SQL SECURITY before VIEW but after the schema for functions/procedures
641        if isinstance(create := properties.parent, exp.Create) and create.kind == "VIEW":
642            post_schema = locations[exp.Properties.Location.POST_SCHEMA]
643            for i, p in enumerate(post_schema):
644                if isinstance(p, exp.SqlSecurityProperty):
645                    post_schema.pop(i)
646                    locations[self.SQL_SECURITY_VIEW_LOCATION].append(p)
647                    break
648
649        return locations
650
651    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
652        persisted = "STORED" if expression.args.get("persisted") else "VIRTUAL"
653        return f"GENERATED ALWAYS AS ({self.sql(expression.this.unnest())}) {persisted}"
654
655    def array_sql(self, expression: exp.Array) -> str:
656        self.unsupported("Arrays are not supported by MySQL")
657        return self.function_fallback_sql(expression)
658
659    def arraycontainsall_sql(self, expression: exp.ArrayContainsAll) -> str:
660        self.unsupported("Array operations are not supported by MySQL")
661        return self.function_fallback_sql(expression)
662
663    def arraycontainedby_sql(self, expression: exp.ArrayContainedBy) -> str:
664        self.unsupported("Array operations are not supported by MySQL")
665        return self.function_fallback_sql(expression)
666
667    def dpipe_sql(self, expression: exp.DPipe) -> str:
668        return self.func("CONCAT", *expression.flatten())
669
670    def intdiv_sql(self, expression: exp.IntDiv) -> str:
671        this = self.sql(exp._wrap(expression.this, exp.Binary))
672        divisor = self.sql(exp._wrap(expression.expression, exp.Binary))
673        sql = f"{this} {self.maybe_comment('DIV', comments=expression.comments)} {divisor}"
674
675        parent = expression.parent
676        if isinstance(parent, (exp.Neg, exp.BitwiseNot)) or (
677            isinstance(parent, (exp.Mul, exp.Div, exp.IntDiv, exp.Mod))
678            and parent.expression is expression
679        ):
680            return f"({sql})"
681
682        return sql
683
684    def extract_sql(self, expression: exp.Extract) -> str:
685        unit = expression.name
686        if unit and unit.lower() == "epoch":
687            return self.func("UNIX_TIMESTAMP", expression.expression)
688
689        return super().extract_sql(expression)
690
691    def datatype_sql(self, expression: exp.DataType) -> str:
692        if (
693            self.VARCHAR_REQUIRES_SIZE
694            and expression.is_type(exp.DType.VARCHAR)
695            and not expression.expressions
696        ):
697            # `VARCHAR` must always have a size - if it doesn't, we always generate `TEXT`
698            return "TEXT"
699
700        # https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
701        result = super().datatype_sql(expression)
702        if expression.this in self.UNSIGNED_TYPE_MAPPING:
703            result = f"{result} UNSIGNED"
704
705        return result
706
707    def jsonarraycontains_sql(self, expression: exp.JSONArrayContains) -> str:
708        return f"{self.sql(expression, 'this')} MEMBER OF({self.sql(expression, 'expression')})"
709
710    def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str:
711        if expression.to.this in self.TIMESTAMP_FUNC_TYPES:
712            return self.func("TIMESTAMP", expression.this)
713
714        to = self.CAST_MAPPING.get(expression.to.this)
715
716        if to:
717            expression.to.set("this", to)
718        return super().cast_sql(expression)
719
720    def show_sql(self, expression: exp.Show) -> str:
721        this = f" {expression.name}"
722        full = " FULL" if expression.args.get("full") else ""
723        global_ = " GLOBAL" if expression.args.get("global_") else ""
724
725        target = self.sql(expression, "target")
726
727        # SHOW CREATE ... takes a possibly qualified name, it has no FROM clause
728        # https://dev.mysql.com/doc/refman/8.4/en/show-create-table.html
729        if expression.name.startswith("CREATE") and (db_id := self.sql(expression, "db")):
730            target = f"{db_id}.{target}"
731            db = ""
732        else:
733            db = self._prefixed_sql("FROM", expression, "db")
734
735        target = f" {target}" if target else ""
736        if expression.name in ("COLUMNS", "INDEX"):
737            target = f" FROM{target}"
738        elif expression.name == "GRANTS":
739            target = f" FOR{target}"
740        elif expression.name in ("LINKS", "PARTITIONS"):
741            target = f" ON{target}" if target else ""
742        elif expression.name == "PROJECTIONS":
743            target = f" ON TABLE{target}" if target else ""
744
745        like = self._prefixed_sql("LIKE", expression, "like")
746        where = self.sql(expression, "where")
747
748        types = self.expressions(expression, key="types")
749        types = f" {types}" if types else types
750        query = self._prefixed_sql("FOR QUERY", expression, "query")
751
752        if expression.name == "PROFILE":
753            offset = self._prefixed_sql("OFFSET", expression, "offset")
754            limit = self._prefixed_sql("LIMIT", expression, "limit")
755        else:
756            offset = ""
757            limit = self._oldstyle_limit_sql(expression)
758
759        log = self._prefixed_sql("IN", expression, "log")
760        position = self._prefixed_sql("FROM", expression, "position")
761
762        channel = self._prefixed_sql("FOR CHANNEL", expression, "channel")
763
764        if expression.name == "ENGINE":
765            mutex_or_status = " MUTEX" if expression.args.get("mutex") else " STATUS"
766        else:
767            mutex_or_status = ""
768
769        for_table = self._prefixed_sql("FOR TABLE", expression, "for_table")
770        for_group = self._prefixed_sql("FOR GROUP", expression, "for_group")
771        for_user = self._prefixed_sql("FOR USER", expression, "for_user")
772        for_role = self._prefixed_sql("FOR ROLE", expression, "for_role")
773        into_outfile = self._prefixed_sql("INTO OUTFILE", expression, "into_outfile")
774        json = " JSON" if expression.args.get("json") else ""
775
776        return f"SHOW{full}{global_}{this}{json}{target}{for_table}{types}{db}{query}{log}{position}{channel}{mutex_or_status}{like}{where}{offset}{limit}{for_group}{for_user}{for_role}{into_outfile}"
777
778    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
779        """To avoid TO keyword in ALTER ... RENAME statements.
780        It's moved from Doris, because it's the same for all MySQL, Doris, and StarRocks.
781        """
782        return super().alterrename_sql(expression, include_to=False)
783
784    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
785        dtype = self.sql(expression, "dtype")
786        if not dtype:
787            return super().altercolumn_sql(expression)
788
789        if expression.args.get("exists"):
790            self.unsupported("ALTER COLUMN IF EXISTS is not supported by this dialect")
791
792        this = self.sql(expression, "this")
793        null_constraint = self._alter_column_null_constraint_sql(expression)
794        return f"MODIFY COLUMN {this} {dtype}{null_constraint}"
795
796    def _prefixed_sql(self, prefix: str, expression: exp.Expr, arg: str) -> str:
797        sql = self.sql(expression, arg)
798        return f" {prefix} {sql}" if sql else ""
799
800    def _oldstyle_limit_sql(self, expression: exp.Show) -> str:
801        limit = self.sql(expression, "limit")
802        offset = self.sql(expression, "offset")
803        if limit:
804            limit_offset = f"{offset}, {limit}" if offset else limit
805            return f" LIMIT {limit_offset}"
806        return ""
807
808    def timestamptrunc_sql(self, expression: exp.TimestampTrunc) -> str:
809        unit = expression.args.get("unit")
810        if isinstance(unit, exp.WeekStart):
811            unit = exp.var(self.weekstart_name(unit))
812
813        # Pick an old-enough date to avoid negative timestamp diffs
814        start_ts = "'0000-01-01 00:00:00'"
815
816        # Source: https://stackoverflow.com/a/32955740
817        timestamp_diff = build_date_delta(exp.TimestampDiff)([unit, start_ts, expression.this])
818        interval = exp.Interval(this=timestamp_diff, unit=unit)
819        dateadd = build_date_delta_with_interval(exp.DateAdd)([start_ts, interval])
820
821        return self.sql(dateadd)
822
823    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
824        from_tz = expression.args.get("source_tz")
825        to_tz = expression.args.get("target_tz")
826        dt = expression.args.get("timestamp")
827
828        return self.func("CONVERT_TZ", dt, from_tz, to_tz)
829
830    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
831        self.unsupported("AT TIME ZONE is not supported by MySQL")
832        return self.sql(expression.this)
833
834    def isascii_sql(self, expression: exp.IsAscii) -> str:
835        return f"REGEXP_LIKE({self.sql(expression.this)}, '^[[:ascii:]]*$')"
836
837    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
838        # https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html
839        self.unsupported("MySQL does not support IGNORE NULLS.")
840        return self.sql(expression.this)
841
842    @unsupported_args("this")
843    def currentschema_sql(self, expression: exp.CurrentSchema) -> str:
844        return self.func("SCHEMA")
845
846    def partition_sql(self, expression: exp.Partition) -> str:
847        parent = expression.parent
848        if isinstance(parent, (exp.PartitionByRangeProperty, exp.PartitionByListProperty)):
849            return self.expressions(expression, flat=True)
850        return super().partition_sql(expression)
851
852    def _partition_by_sql(
853        self, expression: exp.PartitionByRangeProperty | exp.PartitionByListProperty, kind: str
854    ) -> str:
855        partitions = self.expressions(expression, key="partition_expressions", flat=True)
856        create = self.expressions(expression, key="create_expressions", flat=True)
857        return f"PARTITION BY {kind} ({partitions}) ({create})"
858
859    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
860        return self._partition_by_sql(expression, "RANGE")
861
862    def partitionbylistproperty_sql(self, expression: exp.PartitionByListProperty) -> str:
863        return self._partition_by_sql(expression, "LIST")
864
865    def partitionlist_sql(self, expression: exp.PartitionList) -> str:
866        name = self.sql(expression, "this")
867        values = self.expressions(expression, flat=True)
868        return f"PARTITION {name} VALUES IN ({values})"
869
870    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
871        name = self.sql(expression, "this")
872        values = self.expressions(expression, flat=True)
873        return f"PARTITION {name} VALUES LESS THAN ({values})"

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
BYTE_STRING_FUNCTION = 'UNHEX'
SELECT_KINDS: tuple[str, ...] = ()
TRY_SUPPORTED = False
SUPPORTS_UESCAPE = False
SUPPORTS_DECODE_CASE = False
SUPPORTS_MODIFY_COLUMN = True
SUPPORTS_CHANGE_COLUMN = True
SUPPORTS_ALTER_COLUMN_NULLABILITY = True
AFTER_HAVING_MODIFIER_TRANSFORMS = {'windows': <function <lambda>>, 'qualify': <function <lambda>>}
INTERVAL_ALLOWS_PLURAL_FORM = False
LOCKING_READS_SUPPORTED = True
CAST_TO_INT_ROUNDS = True
NULL_ORDERING_SUPPORTED: bool | None = None
JOIN_HINTS = False
TABLE_HINTS = True
DUPLICATE_KEY_UPDATE_WITH_SET = False
QUERY_HINT_SEP = ' '
VALUES_AS_TABLE = False
NVL2_SUPPORTED = False
LAST_DAY_SUPPORTS_DATE_PART = False
JSON_TYPE_REQUIRED_FOR_EXTRACTION = True
JSON_PATH_BRACKETED_KEY_SUPPORTED = False
JSON_KEY_VALUE_PAIR_SEP = ','
SUPPORTS_TO_NUMBER = False
PARSE_JSON_NAME: str | None = None
PAD_FILL_PATTERN_IS_REQUIRED = True
WRAP_DERIVED_VALUES = False
VARCHAR_REQUIRES_SIZE = True
SUPPORTS_MEDIAN = False
UPDATE_STATEMENT_SUPPORTS_FROM = False
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.CredentialsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.CurrentCatalog'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.SessionUser'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.constraints.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ApiProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ApplicationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.CatalogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ComputeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.DatabaseProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.EmptyProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.EndStatement'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.EnviromentProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.HandlerProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ParameterStyleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Except'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.math.Floor'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Get'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.HybridProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.query.Intersect'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.datatypes.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.functions.Int64'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBContainsAnyTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.JSONBContainsAllTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.json.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 Generator.<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 rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.UtcTimestamp'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.Variadic'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.array.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.WithProcedureOptions'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.constraints.WithOperator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.properties.ForceProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.aggregate.ArrayAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseAndAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseOrAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseXorAgg'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.math.BitwiseCount'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.Chr'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.CurrentDate'>: <function no_paren_current_date_sql>, <class 'sqlglot.expressions.functions.CurrentVersion'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.DateDiff'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.temporal.DateAdd'>: <function date_add_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.DateStrToDate'>: <function datestrtodate_sql>, <class 'sqlglot.expressions.temporal.DateSub'>: <function date_add_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.DateTrunc'>: <function _date_trunc_sql>, <class 'sqlglot.expressions.temporal.Day'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.temporal.DayOfMonth'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.temporal.DayOfWeek'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.temporal.DayOfYear'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.aggregate.GroupConcat'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.core.ILike'>: <function no_ilike_sql>, <class 'sqlglot.expressions.functions.If'>: <function if_sql.<locals>._if_sql>, <class 'sqlglot.expressions.json.JSONExtractScalar'>: <function arrow_json_extract_sql>, <class 'sqlglot.expressions.string.Length'>: <function length_or_char_length_sql>, <class 'sqlglot.expressions.aggregate.LogicalOr'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.LogicalAnd'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.Max'>: <function max_or_greatest>, <class 'sqlglot.expressions.aggregate.Min'>: <function min_or_least>, <class 'sqlglot.expressions.temporal.Month'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.core.NullSafeEQ'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.core.NullSafeNEQ'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.string.NumberToStr'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.Pivot'>: <function no_pivot_sql>, <class 'sqlglot.expressions.query.Select'>: <function preprocess.<locals>._to_sql>, <class 'sqlglot.expressions.aggregate.Stddev'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.string.StrPosition'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.StrToDate'>: <function _str_to_date_sql>, <class 'sqlglot.expressions.temporal.StrToTime'>: <function _str_to_date_sql>, <class 'sqlglot.expressions.string.Stuff'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.query.TableSample'>: <function no_tablesample_sql>, <class 'sqlglot.expressions.temporal.TimeFromParts'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TimestampAdd'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TimestampDiff'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TimestampSub'>: <function date_add_interval_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TimeStrToUnix'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.TimeStrToTime'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TimeToStr'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.string.Trim'>: <function trim_sql>, <class 'sqlglot.expressions.math.Trunc'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.functions.TryCast'>: <function no_trycast_sql>, <class 'sqlglot.expressions.temporal.TsOrDsAdd'>: <function date_add_sql.<locals>.func>, <class 'sqlglot.expressions.temporal.TsOrDsDiff'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.TsOrDsToDate'>: <function _ts_or_ds_to_date_sql>, <class 'sqlglot.expressions.string.Unicode'>: <function MySQLGenerator.<lambda>>, <class 'sqlglot.expressions.temporal.UnixToTime'>: <function _unix_to_time_sql>, <class 'sqlglot.expressions.aggregate.Variance'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.aggregate.VariancePop'>: <function rename_func.<locals>.<lambda>>, <class 'sqlglot.expressions.temporal.Week'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.temporal.WeekOfYear'>: <function remove_ts_or_ds_to_date.<locals>.func>, <class 'sqlglot.expressions.temporal.Year'>: <function remove_ts_or_ds_to_date.<locals>.func>}
UNSIGNED_TYPE_MAPPING: ClassVar = {<DType.UBIGINT: 'UBIGINT'>: 'BIGINT', <DType.UINT: 'UINT'>: 'INT', <DType.UMEDIUMINT: 'UMEDIUMINT'>: 'MEDIUMINT', <DType.USMALLINT: 'USMALLINT'>: 'SMALLINT', <DType.UTINYINT: 'UTINYINT'>: 'TINYINT', <DType.UDECIMAL: 'UDECIMAL'>: 'DECIMAL', <DType.UDOUBLE: 'UDOUBLE'>: 'DOUBLE'}
TIMESTAMP_TYPE_MAPPING: ClassVar = {<DType.DATETIME2: 'DATETIME2'>: 'DATETIME', <DType.SMALLDATETIME: 'SMALLDATETIME'>: 'DATETIME', <DType.TIMESTAMP: 'TIMESTAMP'>: 'DATETIME', <DType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>: 'DATETIME', <DType.TIMESTAMPTZ: 'TIMESTAMPTZ'>: 'TIMESTAMP', <DType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>: 'TIMESTAMP'}
TYPE_MAPPING: ClassVar = {<DType.NCHAR: 'NCHAR'>: 'CHAR', <DType.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <DType.INET: 'INET'>: 'INET', <DType.ROWVERSION: 'ROWVERSION'>: 'VARBINARY', <DType.UBIGINT: 'UBIGINT'>: 'BIGINT', <DType.UINT: 'UINT'>: 'INT', <DType.UMEDIUMINT: 'UMEDIUMINT'>: 'MEDIUMINT', <DType.USMALLINT: 'USMALLINT'>: 'SMALLINT', <DType.UTINYINT: 'UTINYINT'>: 'TINYINT', <DType.UDECIMAL: 'UDECIMAL'>: 'DECIMAL', <DType.UDOUBLE: 'UDOUBLE'>: 'DOUBLE', <DType.DATETIME2: 'DATETIME2'>: 'DATETIME', <DType.SMALLDATETIME: 'SMALLDATETIME'>: 'DATETIME', <DType.TIMESTAMP: 'TIMESTAMP'>: 'DATETIME', <DType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>: 'DATETIME', <DType.TIMESTAMPTZ: 'TIMESTAMPTZ'>: 'TIMESTAMP', <DType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>: 'TIMESTAMP'}
PROPERTIES_LOCATION: ClassVar = {<class 'sqlglot.expressions.properties.AggregateKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AllowedValuesProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AlgorithmProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApiProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ApplicationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.AutoIncrementProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.AutoRefreshProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BackupProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.BlockCompressionProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CalledOnNullInputProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.CatalogProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CharacterSetProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ChecksumProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.CollateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ComputeProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.CopyGrantsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.query.Cluster'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ClusteredByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ClusterProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DistributedByProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DuplicateKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DataBlocksizeProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.DatabaseProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DataDeletionProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DefinerProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DictRange'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DynamicProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.DistKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.DistStyleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EmptyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EncodeProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.EngineProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.EnviromentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.HandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ParameterStyleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ExecuteAsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ExternalProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.FallbackProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.FileFormatProperty'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.FreespaceProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.GlobalProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.HeapProperty'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.HybridProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.InheritsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IcebergProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.IncludeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.InputModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.IsolatedLoadingProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.JournalProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.LanguageProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LikeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LocationProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LockProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.LockingProperty'>: <PropertiesLocation.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.properties.LogProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.MaskingProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.MaterializedProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.MergeBlockRatioProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.ModuleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.NetworkProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.NoPrimaryIndexProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.OnProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.OnCommitProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.query.Order'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.OutputModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.PartitionedByProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <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_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RefreshTriggerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RemoteWithConnectionModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ReturnsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RollupProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowAccessProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.RowFormatProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatDelimitedProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.RowFormatSerdeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SampleProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SchemaCommentProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SecureProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SecurityIntegrationProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SerdeProperties'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.Set'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SettingsProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SetProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.SetConfigProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SharingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.SequenceProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.ddl.TriggerProperties'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.SortKeyProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlReadWriteProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.SqlSecurityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StabilityProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StorageHandlerProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.StorageProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.StreamingTableProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.StrictProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.Tags'>: <PropertiesLocation.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.properties.TableSpaceProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.TemporaryProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.ToTableProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.TransientProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.TransformModelProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ddl.MergeTreeTTL'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.UnloggedProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.UsingProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.UsingTemplateProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ViewAttributeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.VirtualProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.VolatileProperty'>: <PropertiesLocation.UNSUPPORTED: 'UNSUPPORTED'>, <class 'sqlglot.expressions.properties.WithDataProperty'>: <PropertiesLocation.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.properties.WithJournalTableProperty'>: <PropertiesLocation.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.properties.WithProcedureOptions'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSchemaBindingProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.WithSystemVersioningProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.ForceProperty'>: <PropertiesLocation.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.properties.PartitionByRangeProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.properties.PartitionByListProperty'>: <PropertiesLocation.POST_SCHEMA: 'POST_SCHEMA'>}
LIMIT_FETCH = 'LIMIT'
LIMIT_ONLY_LITERALS = True
CHAR_CAST_MAPPING: ClassVar = {<DType.LONGTEXT: 'LONGTEXT'>: 'CHAR', <DType.LONGBLOB: 'LONGBLOB'>: 'CHAR', <DType.MEDIUMBLOB: 'MEDIUMBLOB'>: 'CHAR', <DType.MEDIUMTEXT: 'MEDIUMTEXT'>: 'CHAR', <DType.TEXT: 'TEXT'>: 'CHAR', <DType.TINYBLOB: 'TINYBLOB'>: 'CHAR', <DType.TINYTEXT: 'TINYTEXT'>: 'CHAR', <DType.VARCHAR: 'VARCHAR'>: 'CHAR'}
SIGNED_CAST_MAPPING: ClassVar = {<DType.BIGINT: 'BIGINT'>: 'SIGNED', <DType.BOOLEAN: 'BOOLEAN'>: 'SIGNED', <DType.INT: 'INT'>: 'SIGNED', <DType.SMALLINT: 'SMALLINT'>: 'SIGNED', <DType.TINYINT: 'TINYINT'>: 'SIGNED', <DType.MEDIUMINT: 'MEDIUMINT'>: 'SIGNED'}
CAST_MAPPING: ClassVar = {<DType.LONGTEXT: 'LONGTEXT'>: 'CHAR', <DType.LONGBLOB: 'LONGBLOB'>: 'CHAR', <DType.MEDIUMBLOB: 'MEDIUMBLOB'>: 'CHAR', <DType.MEDIUMTEXT: 'MEDIUMTEXT'>: 'CHAR', <DType.TEXT: 'TEXT'>: 'CHAR', <DType.TINYBLOB: 'TINYBLOB'>: 'CHAR', <DType.TINYTEXT: 'TINYTEXT'>: 'CHAR', <DType.VARCHAR: 'VARCHAR'>: 'CHAR', <DType.BIGINT: 'BIGINT'>: 'SIGNED', <DType.BOOLEAN: 'BOOLEAN'>: 'SIGNED', <DType.INT: 'INT'>: 'SIGNED', <DType.SMALLINT: 'SMALLINT'>: 'SIGNED', <DType.TINYINT: 'TINYINT'>: 'SIGNED', <DType.MEDIUMINT: 'MEDIUMINT'>: 'SIGNED', <DType.UBIGINT: 'UBIGINT'>: 'UNSIGNED'}
TIMESTAMP_FUNC_TYPES: ClassVar = {<DType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <DType.TIMESTAMPTZ: 'TIMESTAMPTZ'>}
RESERVED_KEYWORDS = {'optimize', 'outfile', 'low_priority', 'check', 'int', 'ignore', 'tinyint', 'character', 'where', 'json_table', 'distinct', 'group', 'null', 'key', 'using', 'delete', 'generated', 'numeric', 'show', 'undo', 'unsigned', 'foreign', 'unlock', 'lock', 'optimizer_costs', 'high_priority', 'union', 'trailing', 'as', 'hour_second', 'except', 'minute_microsecond', 'sql_small_result', 'by', 'cross', 'hour_minute', 'natural', 'groups', 'master_bind', 'hour_microsecond', 'ntile', 'last_value', 'day_second', 'schemas', 'load', 'trigger', 'cube', 'constraint', 'window', 'loop', 'not', 'no_write_to_binlog', 'localtime', 'sensitive', 'declare', 'percent_rank', 'exit', 'is', 'precision', 'use', 'int4', 'day_hour', 'asensitive', 'column', 'int1', 'then', 'require', 'if', 'match', 'keys', 'with', 'databases', 'add', 'each', 'middleint', 'update', 'int8', 'continue', 'sqlstate', 'smallint', 'grouping', 'terminated', 'div', 'analyze', 'false', 'ssl', 'binary', 'mediumblob', 'sql', 'call', 'primary', 'delayed', 'left', 'fulltext', 'else', 'case', 'enclosed', 'longtext', 'sql_big_result', 'desc', 'all', 'intersect', 'float', 'both', 'limit', 'database', 'recursive', 'elseif', 'or', 'io_before_gtids', 'create', 'deterministic', 'row_number', 'real', 'explain', 'current_timestamp', 'current_user', 'longblob', 'function', 'read_write', 'lead', 'optionally', 'usage', 'order', 'localtimestamp', 'grant', 'tinytext', 'dual', 'day_microsecond', 'first_value', 'stored', 'asc', 'signal', 'restrict', 'exists', 'index', 'range', 'utc_date', 'references', 'cascade', 'out', 'condition', 'varcharacter', 'repeat', 'purge', 'empty', 'char', 'outer', 'describe', 'mediumtext', 'maxvalue', 'distinctrow', 'starting', 'of', 'read', 'regexp', 'virtual', 'and', 'on', 'when', 'mediumint', 'rename', 'lines', 'option', 'accessible', 'xor', 'drop', 'decimal', 'double', 'float4', 'year_month', 'between', 'to', 'specific', 'set', 'zerofill', 'integer', 'dense_rank', 'io_after_gtids', 'over', 'interval', 'mod', 'reads', 'change', 'like', 'current_date', 'procedure', 'int2', 'return', 'before', 'partition', 'table', 'schema', 'bigint', 'varbinary', 'second_microsecond', 'minute_second', 'while', 'in', 'write', 'into', 'release', 'true', 'int3', 'rows', 'revoke', 'varchar', 'infile', 'rank', 'fetch', 'dec', 'resignal', 'inner', 'nth_value', 'leading', 'utc_time', 'leave', 'current_time', 'collate', 'unique', 'varying', 'values', 'blob', 'get', 'cursor', 'day_minute', 'sqlexception', 'insert', 'for', 'insensitive', 'sqlwarning', 'from', 'select', 'default', 'master_ssl_verify_server_cert', 'inout', 'having', 'lag', 'rlike', 'straight_join', 'alter', 'kill', 'right', 'separator', 'lateral', 'float8', 'cume_dist', 'row', 'utc_timestamp', 'convert', 'tinyblob', 'iterate', 'sql_calc_found_rows', 'replace', 'spatial', 'linear', 'modifies', 'join', 'long', 'escaped', 'system', 'force'}
SQL_SECURITY_VIEW_LOCATION: ClassVar = <PropertiesLocation.POST_CREATE: 'POST_CREATE'>
def makeinterval_sql( self: MySQLGenerator, expression: sqlglot.expressions.temporal.MakeInterval) -> str:
613    def makeinterval_sql(self: MySQLGenerator, expression: exp.MakeInterval) -> str:
614        intervals: list[exp.Interval] = []
615        for arg_key, value in expression.args.items():
616            if value is None:
617                continue
618
619            if isinstance(value, exp.Kwarg):
620                unit_name = _MAKE_INTERVAL_UNIT_ALIASES.get(
621                    value.this.name.lower(), value.this.name.lower()
622                )
623                value = value.expression
624            else:
625                unit_name = arg_key
626
627            intervals.append(exp.Interval(this=value.copy(), unit=exp.var(unit_name.upper())))
628
629        if not intervals:
630            return self.function_fallback_sql(expression)
631
632        parent = expression.parent
633        sep = " - " if isinstance(parent, exp.Sub) and parent.expression is expression else " + "
634
635        return sep.join(self.sql(interval) for interval in intervals)
def locate_properties( self, properties: sqlglot.expressions.properties.Properties) -> collections.defaultdict:
637    def locate_properties(self, properties: exp.Properties) -> defaultdict:
638        locations = super().locate_properties(properties)
639
640        # MySQL puts SQL SECURITY before VIEW but after the schema for functions/procedures
641        if isinstance(create := properties.parent, exp.Create) and create.kind == "VIEW":
642            post_schema = locations[exp.Properties.Location.POST_SCHEMA]
643            for i, p in enumerate(post_schema):
644                if isinstance(p, exp.SqlSecurityProperty):
645                    post_schema.pop(i)
646                    locations[self.SQL_SECURITY_VIEW_LOCATION].append(p)
647                    break
648
649        return locations
def computedcolumnconstraint_sql( self, expression: sqlglot.expressions.constraints.ComputedColumnConstraint) -> str:
651    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
652        persisted = "STORED" if expression.args.get("persisted") else "VIRTUAL"
653        return f"GENERATED ALWAYS AS ({self.sql(expression.this.unnest())}) {persisted}"
def array_sql(self, expression: sqlglot.expressions.array.Array) -> str:
655    def array_sql(self, expression: exp.Array) -> str:
656        self.unsupported("Arrays are not supported by MySQL")
657        return self.function_fallback_sql(expression)
def arraycontainsall_sql(self, expression: sqlglot.expressions.array.ArrayContainsAll) -> str:
659    def arraycontainsall_sql(self, expression: exp.ArrayContainsAll) -> str:
660        self.unsupported("Array operations are not supported by MySQL")
661        return self.function_fallback_sql(expression)
def arraycontainedby_sql(self, expression: sqlglot.expressions.array.ArrayContainedBy) -> str:
663    def arraycontainedby_sql(self, expression: exp.ArrayContainedBy) -> str:
664        self.unsupported("Array operations are not supported by MySQL")
665        return self.function_fallback_sql(expression)
def dpipe_sql(self, expression: sqlglot.expressions.core.DPipe) -> str:
667    def dpipe_sql(self, expression: exp.DPipe) -> str:
668        return self.func("CONCAT", *expression.flatten())
def intdiv_sql(self, expression: sqlglot.expressions.core.IntDiv) -> str:
670    def intdiv_sql(self, expression: exp.IntDiv) -> str:
671        this = self.sql(exp._wrap(expression.this, exp.Binary))
672        divisor = self.sql(exp._wrap(expression.expression, exp.Binary))
673        sql = f"{this} {self.maybe_comment('DIV', comments=expression.comments)} {divisor}"
674
675        parent = expression.parent
676        if isinstance(parent, (exp.Neg, exp.BitwiseNot)) or (
677            isinstance(parent, (exp.Mul, exp.Div, exp.IntDiv, exp.Mod))
678            and parent.expression is expression
679        ):
680            return f"({sql})"
681
682        return sql
def extract_sql(self, expression: sqlglot.expressions.temporal.Extract) -> str:
684    def extract_sql(self, expression: exp.Extract) -> str:
685        unit = expression.name
686        if unit and unit.lower() == "epoch":
687            return self.func("UNIX_TIMESTAMP", expression.expression)
688
689        return super().extract_sql(expression)
def datatype_sql(self, expression: sqlglot.expressions.datatypes.DataType) -> str:
691    def datatype_sql(self, expression: exp.DataType) -> str:
692        if (
693            self.VARCHAR_REQUIRES_SIZE
694            and expression.is_type(exp.DType.VARCHAR)
695            and not expression.expressions
696        ):
697            # `VARCHAR` must always have a size - if it doesn't, we always generate `TEXT`
698            return "TEXT"
699
700        # https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
701        result = super().datatype_sql(expression)
702        if expression.this in self.UNSIGNED_TYPE_MAPPING:
703            result = f"{result} UNSIGNED"
704
705        return result
def jsonarraycontains_sql(self, expression: sqlglot.expressions.json.JSONArrayContains) -> str:
707    def jsonarraycontains_sql(self, expression: exp.JSONArrayContains) -> str:
708        return f"{self.sql(expression, 'this')} MEMBER OF({self.sql(expression, 'expression')})"
def cast_sql( self, expression: sqlglot.expressions.functions.Cast, safe_prefix: str | None = None) -> str:
710    def cast_sql(self, expression: exp.Cast, safe_prefix: str | None = None) -> str:
711        if expression.to.this in self.TIMESTAMP_FUNC_TYPES:
712            return self.func("TIMESTAMP", expression.this)
713
714        to = self.CAST_MAPPING.get(expression.to.this)
715
716        if to:
717            expression.to.set("this", to)
718        return super().cast_sql(expression)
def show_sql(self, expression: sqlglot.expressions.ddl.Show) -> str:
720    def show_sql(self, expression: exp.Show) -> str:
721        this = f" {expression.name}"
722        full = " FULL" if expression.args.get("full") else ""
723        global_ = " GLOBAL" if expression.args.get("global_") else ""
724
725        target = self.sql(expression, "target")
726
727        # SHOW CREATE ... takes a possibly qualified name, it has no FROM clause
728        # https://dev.mysql.com/doc/refman/8.4/en/show-create-table.html
729        if expression.name.startswith("CREATE") and (db_id := self.sql(expression, "db")):
730            target = f"{db_id}.{target}"
731            db = ""
732        else:
733            db = self._prefixed_sql("FROM", expression, "db")
734
735        target = f" {target}" if target else ""
736        if expression.name in ("COLUMNS", "INDEX"):
737            target = f" FROM{target}"
738        elif expression.name == "GRANTS":
739            target = f" FOR{target}"
740        elif expression.name in ("LINKS", "PARTITIONS"):
741            target = f" ON{target}" if target else ""
742        elif expression.name == "PROJECTIONS":
743            target = f" ON TABLE{target}" if target else ""
744
745        like = self._prefixed_sql("LIKE", expression, "like")
746        where = self.sql(expression, "where")
747
748        types = self.expressions(expression, key="types")
749        types = f" {types}" if types else types
750        query = self._prefixed_sql("FOR QUERY", expression, "query")
751
752        if expression.name == "PROFILE":
753            offset = self._prefixed_sql("OFFSET", expression, "offset")
754            limit = self._prefixed_sql("LIMIT", expression, "limit")
755        else:
756            offset = ""
757            limit = self._oldstyle_limit_sql(expression)
758
759        log = self._prefixed_sql("IN", expression, "log")
760        position = self._prefixed_sql("FROM", expression, "position")
761
762        channel = self._prefixed_sql("FOR CHANNEL", expression, "channel")
763
764        if expression.name == "ENGINE":
765            mutex_or_status = " MUTEX" if expression.args.get("mutex") else " STATUS"
766        else:
767            mutex_or_status = ""
768
769        for_table = self._prefixed_sql("FOR TABLE", expression, "for_table")
770        for_group = self._prefixed_sql("FOR GROUP", expression, "for_group")
771        for_user = self._prefixed_sql("FOR USER", expression, "for_user")
772        for_role = self._prefixed_sql("FOR ROLE", expression, "for_role")
773        into_outfile = self._prefixed_sql("INTO OUTFILE", expression, "into_outfile")
774        json = " JSON" if expression.args.get("json") else ""
775
776        return f"SHOW{full}{global_}{this}{json}{target}{for_table}{types}{db}{query}{log}{position}{channel}{mutex_or_status}{like}{where}{offset}{limit}{for_group}{for_user}{for_role}{into_outfile}"
def alterrename_sql( self, expression: sqlglot.expressions.ddl.AlterRename, include_to: bool = True) -> str:
778    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
779        """To avoid TO keyword in ALTER ... RENAME statements.
780        It's moved from Doris, because it's the same for all MySQL, Doris, and StarRocks.
781        """
782        return super().alterrename_sql(expression, include_to=False)

To avoid TO keyword in ALTER ... RENAME statements. It's moved from Doris, because it's the same for all MySQL, Doris, and StarRocks.

def altercolumn_sql(self, expression: sqlglot.expressions.ddl.AlterColumn) -> str:
784    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
785        dtype = self.sql(expression, "dtype")
786        if not dtype:
787            return super().altercolumn_sql(expression)
788
789        if expression.args.get("exists"):
790            self.unsupported("ALTER COLUMN IF EXISTS is not supported by this dialect")
791
792        this = self.sql(expression, "this")
793        null_constraint = self._alter_column_null_constraint_sql(expression)
794        return f"MODIFY COLUMN {this} {dtype}{null_constraint}"
def timestamptrunc_sql(self, expression: sqlglot.expressions.temporal.TimestampTrunc) -> str:
808    def timestamptrunc_sql(self, expression: exp.TimestampTrunc) -> str:
809        unit = expression.args.get("unit")
810        if isinstance(unit, exp.WeekStart):
811            unit = exp.var(self.weekstart_name(unit))
812
813        # Pick an old-enough date to avoid negative timestamp diffs
814        start_ts = "'0000-01-01 00:00:00'"
815
816        # Source: https://stackoverflow.com/a/32955740
817        timestamp_diff = build_date_delta(exp.TimestampDiff)([unit, start_ts, expression.this])
818        interval = exp.Interval(this=timestamp_diff, unit=unit)
819        dateadd = build_date_delta_with_interval(exp.DateAdd)([start_ts, interval])
820
821        return self.sql(dateadd)
def converttimezone_sql(self, expression: sqlglot.expressions.temporal.ConvertTimezone) -> str:
823    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
824        from_tz = expression.args.get("source_tz")
825        to_tz = expression.args.get("target_tz")
826        dt = expression.args.get("timestamp")
827
828        return self.func("CONVERT_TZ", dt, from_tz, to_tz)
def attimezone_sql(self, expression: sqlglot.expressions.core.AtTimeZone) -> str:
830    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
831        self.unsupported("AT TIME ZONE is not supported by MySQL")
832        return self.sql(expression.this)
def isascii_sql(self, expression: sqlglot.expressions.string.IsAscii) -> str:
834    def isascii_sql(self, expression: exp.IsAscii) -> str:
835        return f"REGEXP_LIKE({self.sql(expression.this)}, '^[[:ascii:]]*$')"
def ignorenulls_sql(self, expression: sqlglot.expressions.core.IgnoreNulls) -> str:
837    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
838        # https://dev.mysql.com/doc/refman/8.4/en/window-function-descriptions.html
839        self.unsupported("MySQL does not support IGNORE NULLS.")
840        return self.sql(expression.this)
@unsupported_args('this')
def currentschema_sql(self, expression: sqlglot.expressions.functions.CurrentSchema) -> str:
842    @unsupported_args("this")
843    def currentschema_sql(self, expression: exp.CurrentSchema) -> str:
844        return self.func("SCHEMA")
def partition_sql(self, expression: sqlglot.expressions.query.Partition) -> str:
846    def partition_sql(self, expression: exp.Partition) -> str:
847        parent = expression.parent
848        if isinstance(parent, (exp.PartitionByRangeProperty, exp.PartitionByListProperty)):
849            return self.expressions(expression, flat=True)
850        return super().partition_sql(expression)
def partitionbyrangeproperty_sql( self, expression: sqlglot.expressions.properties.PartitionByRangeProperty) -> str:
859    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
860        return self._partition_by_sql(expression, "RANGE")
def partitionbylistproperty_sql( self, expression: sqlglot.expressions.properties.PartitionByListProperty) -> str:
862    def partitionbylistproperty_sql(self, expression: exp.PartitionByListProperty) -> str:
863        return self._partition_by_sql(expression, "LIST")
def partitionlist_sql(self, expression: sqlglot.expressions.properties.PartitionList) -> str:
865    def partitionlist_sql(self, expression: exp.PartitionList) -> str:
866        name = self.sql(expression, "this")
867        values = self.expressions(expression, flat=True)
868        return f"PARTITION {name} VALUES IN ({values})"
def partitionrange_sql(self, expression: sqlglot.expressions.query.PartitionRange) -> str:
870    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
871        name = self.sql(expression, "this")
872        values = self.expressions(expression, flat=True)
873        return f"PARTITION {name} VALUES LESS THAN ({values})"
Inherited Members
sqlglot.generator.Generator
Generator
WINDOW_FUNCS_WITH_NULL_ORDERING
IGNORE_NULLS_IN_FUNC
IGNORE_NULLS_BEFORE_ORDER
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
CREATE_FUNCTION_RETURN_AS
MATCHED_BY_SOURCE
SUPPORTS_MERGE_WHERE
SINGLE_STRING_INTERVAL
AUTO_REFRESH_BARE_INTERVALS
RENAME_TABLE_WITH_DB
GROUPINGS_SEP
SUPPORTS_GROUPING_SETS_AS_SUFFIX
INDEX_ON
INOUT_SEPARATOR
DIRECTED_JOINS
QUERY_HINTS
IS_BOOL_ALLOWED
SAFE_INT_DIVISION
LIMIT_IS_TOP
RETURNING_END
EXTRACT_ALLOWS_QUOTES
TZ_TO_WITH_TIME_ZONE
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
UNNEST_WITH_ORDINALITY
SEMI_ANTI_JOIN_WITH_SIDE
COMPUTED_COLUMN_WITH_TYPE
SUPPORTS_TABLE_COPY
TABLESAMPLE_REQUIRES_PARENS
TABLESAMPLE_SIZE_IS_ROWS
TABLESAMPLE_KEYWORDS
TABLESAMPLE_WITH_METHOD
TABLESAMPLE_SEED_KEYWORD
HISTORICAL_DATA_POST_ALIAS
COLLATE_IS_FUNC
DATA_TYPE_SPECIFIERS_ALLOWED
ENSURE_BOOLS
CTE_RECURSIVE_KEYWORD_REQUIRED
SUPPORTS_SINGLE_ARG_CONCAT
SUPPORTS_TABLE_ALIAS_COLUMNS
SUPPORTS_NAMED_CTE_COLUMNS
UNPIVOT_ALIASES_ARE_IDENTIFIERS
PIVOT_ALIAS_WITH_AS
INSERT_OVERWRITE
SUPPORTS_SELECT_INTO
SUPPORTS_UNLOGGED_TABLES
SUPPORTS_CREATE_TABLE_LIKE
SUPPORTS_ALTER_COLUMN_IF_EXISTS
LIKE_PROPERTY_INSIDE_SCHEMA
MULTI_ARG_DISTINCT
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
UNICODE_SUBSTITUTE
STAR_EXCEPT
HEX_FUNC
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
ARRAY_SIZE_NAME
ALTER_SET_TYPE
ARRAY_SIZE_DIM_REQUIRED
SUPPORTS_BETWEEN_FLAGS
SUPPORTS_LIKE_QUANTIFIERS
MATCH_AGAINST_TABLE_PREFIX
SET_ASSIGNMENT_REQUIRES_VARIABLE_KEYWORD
DECLARE_DEFAULT_ASSIGNMENT
STAR_EXCLUDE_REQUIRES_DERIVED_TABLE
SUPPORTS_DROP_ALTER_ICEBERG_PROPERTY
UNSUPPORTED_TYPES
TYPE_PARAM_SETTINGS
TIME_PART_SINGULARS
TOKEN_MAPPING
STRUCT_DELIMITER
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
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
autoincrementcolumnconstraint_sql
compresscolumnconstraint_sql
generatedasidentitycolumnconstraint_sql
generatedasrowcolumnconstraint_sql
periodforsystemtimeconstraint_sql
notnullcolumnconstraint_sql
primarykeycolumnconstraint_sql
uniquecolumnconstraint_sql
inoutcolumnconstraint_sql
createable_sql
create_sql
sequenceproperties_sql
triggerproperties_sql
triggerreferencing_sql
triggerevent_sql
clone_sql
describe_sql
heredoc_sql
prepend_ctes
with_sql
cte_sql
tablealias_sql
bitstring_sql
hexstring_sql
bytestring_sql
unicodestring_sql
rawstring_sql
datatypeparam_sql
datatype_param_bound_limiter
directory_sql
delete_sql
drop_sql
set_operation
set_operations
fetch_sql
limitoptions_sql
filter_sql
hint_sql
indexparameters_sql
index_sql
dynamicidentifier_sql
identifier_sql
hex_sql
lowerhex_sql
inputoutputformat_sql
national_sql
properties_sql
root_properties
properties
with_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
connect_sql
prior_sql
join_sql
lambda_sql
lateral_op
lateral_sql
limit_sql
offset_sql
setitem_sql
set_sql
queryband_sql
pragma_sql
lock_sql
literal_sql
escape_str
loaddata_sql
null_sql
boolean_sql
booland_sql
boolor_sql
order_sql
withfill_sql
cluster_sql
clusterproperty_sql
distribute_sql
sort_sql
ordered_sql
matchrecognizemeasure_sql
matchrecognize_sql
query_modifiers
options_modifier
forclause_sql
queryoption_sql
offset_limit_modifiers
after_limit_modifiers
select_sql
schema_sql
schema_columns_sql
star_sql
parameter_sql
sessionparameter_sql
placeholder_sql
subquery_sql
qualify_sql
unnest_sql
prewhere_sql
where_sql
window_sql
partition_by_sql
partitionbyexcluding_sql
windowspec_sql
withingroup_sql
between_sql
bracket_offset_expressions
bracket_sql
all_sql
any_sql
exists_sql
case_sql
constraint_sql
nextvaluefor_sql
trim_sql
convert_concat_args
concat_sql
concatws_sql
check_sql
foreignkey_sql
primarykey_sql
timeserieskey_sql
if_sql
matchagainst_sql
jsonkeyvalue_sql
jsonpath_sql
json_path_part
formatjson_sql
formatphrase_sql
jsonarray_sql
jsonarrayagg_sql
jsoncolumndef_sql
jsonschema_sql
jsontable_sql
openjsoncolumndef_sql
openjson_sql
in_sql
in_unnest_op
interval_sql
return_sql
reference_sql
anonymous_sql
paren_sql
neg_sql
not_sql
alias_sql
pivotalias_sql
aliases_sql
atindex_sql
fromtimezone_sql
fromiso8601date_sql
fromiso8601timestamp_sql
fromiso8601timestampnanos_sql
add_sql
and_sql
or_sql
xor_sql
connector_sql
bitwiseand_sql
bitwiseleftshift_sql
bitwisenot_sql
bitwiseor_sql
bitwiserightshift_sql
bitwisexor_sql
strtotime_sql
strtodate_sql
parsedatetime_sql
currentdate_sql
collate_sql
command_sql
comment_sql
mergetreettlaction_sql
mergetreettl_sql
transaction_sql
commit_sql
rollback_sql
modifycolumn_sql
alterindex_sql
alterdiststyle_sql
altersortkey_sql
renamecolumn_sql
alterset_sql
alter_sql
altersession_sql
add_column_sql
droppartition_sql
dropprimarykey_sql
addconstraint_sql
replacecolumns_sql
addpartition_sql
distinct_sql
respectnulls_sql
havingmax_sql
div_sql
safedivide_sql
overlaps_sql
distance_sql
distancend_sql
dot_sql
eq_sql
propertyeq_sql
escape_sql
glob_sql
gt_sql
gte_sql
is_sql
like_sql
ilike_sql
match_sql
similarto_sql
lt_sql
lte_sql
mod_sql
mul_sql
neq_sql
nullsafeeq_sql
nullsafeneq_sql
sub_sql
trycast_sql
jsoncast_sql
try_sql
log_sql
use_sql
binary
ceil_floor
function_fallback_sql
function_name
func
format_args
too_wide
format_time
expressions
op_expressions
naked_property
tag_sql
token_sql
userdefinedfunction_sql
macrooverloads_sql
macrooverload_sql
joinhint_sql
kwarg_sql
when_sql
whens_sql
merge_sql
tochar_sql
tonumber_sql
dictproperty_sql
dictrange_sql
dictsubproperty_sql
aggregatekeyproperty_sql
duplicatekeyproperty_sql
uniquekeyproperty_sql
distributedbyproperty_sql
oncluster_sql
clusteredbyproperty_sql
anyvalue_sql
querytransform_sql
indexconstraintoption_sql
checkcolumnconstraint_sql
indexcolumnconstraint_sql
nvl2_sql
nthvalue_sql
comprehension_sql
columnprefix_sql
opclass_sql
predict_sql
generateembedding_sql
generatetext_sql
generatetable_sql
generatebool_sql
generateint_sql
generatedouble_sql
mltranslate_sql
mlforecast_sql
aiforecast_sql
featuresattime_sql
vectorsearch_sql
forin_sql
refresh_sql
toarray_sql
tsordstotime_sql
tsordstotimestamp_sql
tsordstodatetime_sql
tsordstodate_sql
unixdate_sql
lastday_sql
dateadd_sql
arrayinsert_sql
arrayany_sql
struct_sql
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
json_sql
jsonvalue_sql
skipjsoncolumn_sql
conditionalinsert_sql
multitableinserts_sql
oncondition_sql
jsonextractquote_sql
jsonexists_sql
arrayagg_sql
slice_sql
apply_sql
grant_sql
revoke_sql
grantprivilege_sql
grantprincipal_sql
columns_sql
overlay_sql
todouble_sql
string_sql
median_sql
overflowtruncatebehavior_sql
unixseconds_sql
arraysort_sql
arraysize_sql
attach_sql
detach_sql
attachoption_sql
watermarkcolumnconstraint_sql
encodeproperty_sql
includeproperty_sql
xmlelement_sql
xmlkeyvalueoption_sql
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
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