sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

       1from __future__ import annotations
       2
       3import logging
       4import re
       5import typing as t
       6from collections import defaultdict
       7from functools import reduce, wraps
       8
       9from sqlglot import exp
      10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
      11from sqlglot.helper import apply_index_offset, csv, name_sequence, seq_get
      12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
      13from sqlglot.time import format_time
      14from sqlglot.tokens import TokenType
      15
      16if t.TYPE_CHECKING:
      17    from sqlglot._typing import E
      18    from sqlglot.dialects.dialect import DialectType
      19
      20    G = t.TypeVar("G", bound="Generator")
      21    GeneratorMethod = t.Callable[[G, E], str]
      22
      23logger = logging.getLogger("sqlglot")
      24
      25ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
      26UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
      27
      28
      29def unsupported_args(
      30    *args: t.Union[str, t.Tuple[str, str]],
      31) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
      32    """
      33    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
      34    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
      35    """
      36    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
      37    for arg in args:
      38        if isinstance(arg, str):
      39            diagnostic_by_arg[arg] = None
      40        else:
      41            diagnostic_by_arg[arg[0]] = arg[1]
      42
      43    def decorator(func: GeneratorMethod) -> GeneratorMethod:
      44        @wraps(func)
      45        def _func(generator: G, expression: E) -> str:
      46            expression_name = expression.__class__.__name__
      47            dialect_name = generator.dialect.__class__.__name__
      48
      49            for arg_name, diagnostic in diagnostic_by_arg.items():
      50                if expression.args.get(arg_name):
      51                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
      52                        arg_name, expression_name, dialect_name
      53                    )
      54                    generator.unsupported(diagnostic)
      55
      56            return func(generator, expression)
      57
      58        return _func
      59
      60    return decorator
      61
      62
      63class _Generator(type):
      64    def __new__(cls, clsname, bases, attrs):
      65        klass = super().__new__(cls, clsname, bases, attrs)
      66
      67        # Remove transforms that correspond to unsupported JSONPathPart expressions
      68        for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS:
      69            klass.TRANSFORMS.pop(part, None)
      70
      71        return klass
      72
      73
      74class Generator(metaclass=_Generator):
      75    """
      76    Generator converts a given syntax tree to the corresponding SQL string.
      77
      78    Args:
      79        pretty: Whether to format the produced SQL string.
      80            Default: False.
      81        identify: Determines when an identifier should be quoted. Possible values are:
      82            False (default): Never quote, except in cases where it's mandatory by the dialect.
      83            True or 'always': Always quote.
      84            'safe': Only quote identifiers that are case insensitive.
      85        normalize: Whether to normalize identifiers to lowercase.
      86            Default: False.
      87        pad: The pad size in a formatted string. For example, this affects the indentation of
      88            a projection in a query, relative to its nesting level.
      89            Default: 2.
      90        indent: The indentation size in a formatted string. For example, this affects the
      91            indentation of subqueries and filters under a `WHERE` clause.
      92            Default: 2.
      93        normalize_functions: How to normalize function names. Possible values are:
      94            "upper" or True (default): Convert names to uppercase.
      95            "lower": Convert names to lowercase.
      96            False: Disables function name normalization.
      97        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
      98            Default ErrorLevel.WARN.
      99        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
     100            This is only relevant if unsupported_level is ErrorLevel.RAISE.
     101            Default: 3
     102        leading_comma: Whether the comma is leading or trailing in select expressions.
     103            This is only relevant when generating in pretty mode.
     104            Default: False
     105        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
     106            The default is on the smaller end because the length only represents a segment and not the true
     107            line length.
     108            Default: 80
     109        comments: Whether to preserve comments in the output SQL code.
     110            Default: True
     111    """
     112
     113    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
     114        **JSON_PATH_PART_TRANSFORMS,
     115        exp.AllowedValuesProperty: lambda self,
     116        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
     117        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
     118        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
     119        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
     120        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
     121        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
     122        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
     123        exp.CaseSpecificColumnConstraint: lambda _,
     124        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
     125        exp.Ceil: lambda self, e: self.ceil_floor(e),
     126        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
     127        exp.CharacterSetProperty: lambda self,
     128        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
     129        exp.ClusteredColumnConstraint: lambda self,
     130        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
     131        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
     132        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
     133        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
     134        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
     135        exp.CredentialsProperty: lambda self,
     136        e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
     137        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
     138        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
     139        exp.DynamicProperty: lambda *_: "DYNAMIC",
     140        exp.EmptyProperty: lambda *_: "EMPTY",
     141        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
     142        exp.EphemeralColumnConstraint: lambda self,
     143        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
     144        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
     145        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
     146        exp.Except: lambda self, e: self.set_operations(e),
     147        exp.ExternalProperty: lambda *_: "EXTERNAL",
     148        exp.Floor: lambda self, e: self.ceil_floor(e),
     149        exp.Get: lambda self, e: self.get_put_sql(e),
     150        exp.GlobalProperty: lambda *_: "GLOBAL",
     151        exp.HeapProperty: lambda *_: "HEAP",
     152        exp.IcebergProperty: lambda *_: "ICEBERG",
     153        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
     154        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
     155        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
     156        exp.Intersect: lambda self, e: self.set_operations(e),
     157        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
     158        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
     159        exp.LanguageProperty: lambda self, e: self.naked_property(e),
     160        exp.LocationProperty: lambda self, e: self.naked_property(e),
     161        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
     162        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
     163        exp.NonClusteredColumnConstraint: lambda self,
     164        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
     165        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
     166        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
     167        exp.OnCommitProperty: lambda _,
     168        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
     169        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
     170        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
     171        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
     172        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
     173        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
     174        exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
     175        exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
     176        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
     177        exp.ProjectionPolicyColumnConstraint: lambda self,
     178        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
     179        exp.Put: lambda self, e: self.get_put_sql(e),
     180        exp.RemoteWithConnectionModelProperty: lambda self,
     181        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
     182        exp.ReturnsProperty: lambda self, e: (
     183            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
     184        ),
     185        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
     186        exp.SecureProperty: lambda *_: "SECURE",
     187        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
     188        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
     189        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
     190        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
     191        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
     192        exp.SqlReadWriteProperty: lambda _, e: e.name,
     193        exp.SqlSecurityProperty: lambda _,
     194        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
     195        exp.StabilityProperty: lambda _, e: e.name,
     196        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
     197        exp.StreamingTableProperty: lambda *_: "STREAMING",
     198        exp.StrictProperty: lambda *_: "STRICT",
     199        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
     200        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
     201        exp.TemporaryProperty: lambda *_: "TEMPORARY",
     202        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     203        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
     204        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
     205        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
     206        exp.TransientProperty: lambda *_: "TRANSIENT",
     207        exp.Union: lambda self, e: self.set_operations(e),
     208        exp.UnloggedProperty: lambda *_: "UNLOGGED",
     209        exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
     210        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
     211        exp.Uuid: lambda *_: "UUID()",
     212        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
     213        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
     214        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
     215        exp.VolatileProperty: lambda *_: "VOLATILE",
     216        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
     217        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
     218        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
     219        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
     220        exp.ForceProperty: lambda *_: "FORCE",
     221    }
     222
     223    # Whether null ordering is supported in order by
     224    # True: Full Support, None: No support, False: No support for certain cases
     225    # such as window specifications, aggregate functions etc
     226    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
     227
     228    # Whether ignore nulls is inside the agg or outside.
     229    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
     230    IGNORE_NULLS_IN_FUNC = False
     231
     232    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     233    LOCKING_READS_SUPPORTED = False
     234
     235    # Whether the EXCEPT and INTERSECT operations can return duplicates
     236    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
     237
     238    # Wrap derived values in parens, usually standard but spark doesn't support it
     239    WRAP_DERIVED_VALUES = True
     240
     241    # Whether create function uses an AS before the RETURN
     242    CREATE_FUNCTION_RETURN_AS = True
     243
     244    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
     245    MATCHED_BY_SOURCE = True
     246
     247    # Whether the INTERVAL expression works only with values like '1 day'
     248    SINGLE_STRING_INTERVAL = False
     249
     250    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     251    INTERVAL_ALLOWS_PLURAL_FORM = True
     252
     253    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     254    LIMIT_FETCH = "ALL"
     255
     256    # Whether limit and fetch allows expresions or just limits
     257    LIMIT_ONLY_LITERALS = False
     258
     259    # Whether a table is allowed to be renamed with a db
     260    RENAME_TABLE_WITH_DB = True
     261
     262    # The separator for grouping sets and rollups
     263    GROUPINGS_SEP = ","
     264
     265    # The string used for creating an index on a table
     266    INDEX_ON = "ON"
     267
     268    # Whether join hints should be generated
     269    JOIN_HINTS = True
     270
     271    # Whether table hints should be generated
     272    TABLE_HINTS = True
     273
     274    # Whether query hints should be generated
     275    QUERY_HINTS = True
     276
     277    # What kind of separator to use for query hints
     278    QUERY_HINT_SEP = ", "
     279
     280    # Whether comparing against booleans (e.g. x IS TRUE) is supported
     281    IS_BOOL_ALLOWED = True
     282
     283    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
     284    DUPLICATE_KEY_UPDATE_WITH_SET = True
     285
     286    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
     287    LIMIT_IS_TOP = False
     288
     289    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
     290    RETURNING_END = True
     291
     292    # Whether to generate an unquoted value for EXTRACT's date part argument
     293    EXTRACT_ALLOWS_QUOTES = True
     294
     295    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
     296    TZ_TO_WITH_TIME_ZONE = False
     297
     298    # Whether the NVL2 function is supported
     299    NVL2_SUPPORTED = True
     300
     301    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
     302    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
     303
     304    # Whether VALUES statements can be used as derived tables.
     305    # MySQL 5 and Redshift do not allow this, so when False, it will convert
     306    # SELECT * VALUES into SELECT UNION
     307    VALUES_AS_TABLE = True
     308
     309    # Whether the word COLUMN is included when adding a column with ALTER TABLE
     310    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
     311
     312    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     313    UNNEST_WITH_ORDINALITY = True
     314
     315    # Whether FILTER (WHERE cond) can be used for conditional aggregation
     316    AGGREGATE_FILTER_SUPPORTED = True
     317
     318    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
     319    SEMI_ANTI_JOIN_WITH_SIDE = True
     320
     321    # Whether to include the type of a computed column in the CREATE DDL
     322    COMPUTED_COLUMN_WITH_TYPE = True
     323
     324    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
     325    SUPPORTS_TABLE_COPY = True
     326
     327    # Whether parentheses are required around the table sample's expression
     328    TABLESAMPLE_REQUIRES_PARENS = True
     329
     330    # Whether a table sample clause's size needs to be followed by the ROWS keyword
     331    TABLESAMPLE_SIZE_IS_ROWS = True
     332
     333    # The keyword(s) to use when generating a sample clause
     334    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
     335
     336    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
     337    TABLESAMPLE_WITH_METHOD = True
     338
     339    # The keyword to use when specifying the seed of a sample clause
     340    TABLESAMPLE_SEED_KEYWORD = "SEED"
     341
     342    # Whether COLLATE is a function instead of a binary operator
     343    COLLATE_IS_FUNC = False
     344
     345    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
     346    DATA_TYPE_SPECIFIERS_ALLOWED = False
     347
     348    # Whether conditions require booleans WHERE x = 0 vs WHERE x
     349    ENSURE_BOOLS = False
     350
     351    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
     352    CTE_RECURSIVE_KEYWORD_REQUIRED = True
     353
     354    # Whether CONCAT requires >1 arguments
     355    SUPPORTS_SINGLE_ARG_CONCAT = True
     356
     357    # Whether LAST_DAY function supports a date part argument
     358    LAST_DAY_SUPPORTS_DATE_PART = True
     359
     360    # Whether named columns are allowed in table aliases
     361    SUPPORTS_TABLE_ALIAS_COLUMNS = True
     362
     363    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
     364    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
     365
     366    # What delimiter to use for separating JSON key/value pairs
     367    JSON_KEY_VALUE_PAIR_SEP = ":"
     368
     369    # INSERT OVERWRITE TABLE x override
     370    INSERT_OVERWRITE = " OVERWRITE TABLE"
     371
     372    # Whether the SELECT .. INTO syntax is used instead of CTAS
     373    SUPPORTS_SELECT_INTO = False
     374
     375    # Whether UNLOGGED tables can be created
     376    SUPPORTS_UNLOGGED_TABLES = False
     377
     378    # Whether the CREATE TABLE LIKE statement is supported
     379    SUPPORTS_CREATE_TABLE_LIKE = True
     380
     381    # Whether the LikeProperty needs to be specified inside of the schema clause
     382    LIKE_PROPERTY_INSIDE_SCHEMA = False
     383
     384    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
     385    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
     386    MULTI_ARG_DISTINCT = True
     387
     388    # Whether the JSON extraction operators expect a value of type JSON
     389    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
     390
     391    # Whether bracketed keys like ["foo"] are supported in JSON paths
     392    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
     393
     394    # Whether to escape keys using single quotes in JSON paths
     395    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
     396
     397    # The JSONPathPart expressions supported by this dialect
     398    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
     399
     400    # Whether any(f(x) for x in array) can be implemented by this dialect
     401    CAN_IMPLEMENT_ARRAY_ANY = False
     402
     403    # Whether the function TO_NUMBER is supported
     404    SUPPORTS_TO_NUMBER = True
     405
     406    # Whether or not set op modifiers apply to the outer set op or select.
     407    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
     408    # True means limit 1 happens after the set op, False means it it happens on y.
     409    SET_OP_MODIFIERS = True
     410
     411    # Whether parameters from COPY statement are wrapped in parentheses
     412    COPY_PARAMS_ARE_WRAPPED = True
     413
     414    # Whether values of params are set with "=" token or empty space
     415    COPY_PARAMS_EQ_REQUIRED = False
     416
     417    # Whether COPY statement has INTO keyword
     418    COPY_HAS_INTO_KEYWORD = True
     419
     420    # Whether the conditional TRY(expression) function is supported
     421    TRY_SUPPORTED = True
     422
     423    # Whether the UESCAPE syntax in unicode strings is supported
     424    SUPPORTS_UESCAPE = True
     425
     426    # The keyword to use when generating a star projection with excluded columns
     427    STAR_EXCEPT = "EXCEPT"
     428
     429    # The HEX function name
     430    HEX_FUNC = "HEX"
     431
     432    # The keywords to use when prefixing & separating WITH based properties
     433    WITH_PROPERTIES_PREFIX = "WITH"
     434
     435    # Whether to quote the generated expression of exp.JsonPath
     436    QUOTE_JSON_PATH = True
     437
     438    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
     439    PAD_FILL_PATTERN_IS_REQUIRED = False
     440
     441    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
     442    SUPPORTS_EXPLODING_PROJECTIONS = True
     443
     444    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
     445    ARRAY_CONCAT_IS_VAR_LEN = True
     446
     447    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
     448    SUPPORTS_CONVERT_TIMEZONE = False
     449
     450    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
     451    SUPPORTS_MEDIAN = True
     452
     453    # Whether UNIX_SECONDS(timestamp) is supported
     454    SUPPORTS_UNIX_SECONDS = False
     455
     456    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
     457    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
     458
     459    # The function name of the exp.ArraySize expression
     460    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
     461
     462    # The syntax to use when altering the type of a column
     463    ALTER_SET_TYPE = "SET DATA TYPE"
     464
     465    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
     466    # None -> Doesn't support it at all
     467    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
     468    # True (Postgres) -> Explicitly requires it
     469    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
     470
     471    TYPE_MAPPING = {
     472        exp.DataType.Type.DATETIME2: "TIMESTAMP",
     473        exp.DataType.Type.NCHAR: "CHAR",
     474        exp.DataType.Type.NVARCHAR: "VARCHAR",
     475        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     476        exp.DataType.Type.LONGTEXT: "TEXT",
     477        exp.DataType.Type.TINYTEXT: "TEXT",
     478        exp.DataType.Type.BLOB: "VARBINARY",
     479        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     480        exp.DataType.Type.LONGBLOB: "BLOB",
     481        exp.DataType.Type.TINYBLOB: "BLOB",
     482        exp.DataType.Type.INET: "INET",
     483        exp.DataType.Type.ROWVERSION: "VARBINARY",
     484        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
     485    }
     486
     487    TIME_PART_SINGULARS = {
     488        "MICROSECONDS": "MICROSECOND",
     489        "SECONDS": "SECOND",
     490        "MINUTES": "MINUTE",
     491        "HOURS": "HOUR",
     492        "DAYS": "DAY",
     493        "WEEKS": "WEEK",
     494        "MONTHS": "MONTH",
     495        "QUARTERS": "QUARTER",
     496        "YEARS": "YEAR",
     497    }
     498
     499    AFTER_HAVING_MODIFIER_TRANSFORMS = {
     500        "cluster": lambda self, e: self.sql(e, "cluster"),
     501        "distribute": lambda self, e: self.sql(e, "distribute"),
     502        "sort": lambda self, e: self.sql(e, "sort"),
     503        "windows": lambda self, e: (
     504            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
     505            if e.args.get("windows")
     506            else ""
     507        ),
     508        "qualify": lambda self, e: self.sql(e, "qualify"),
     509    }
     510
     511    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     512
     513    STRUCT_DELIMITER = ("<", ">")
     514
     515    PARAMETER_TOKEN = "@"
     516    NAMED_PLACEHOLDER_TOKEN = ":"
     517
     518    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
     519
     520    PROPERTIES_LOCATION = {
     521        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
     522        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     523        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     524        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
     525        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
     526        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     527        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     528        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     529        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     530        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
     531        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     532        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
     533        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
     534        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
     535        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     536        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
     537        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     538        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     539        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     540        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
     541        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     542        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     543        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
     544        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
     545        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     546        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     547        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     548        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     549        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     550        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     551        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
     552        exp.HeapProperty: exp.Properties.Location.POST_WITH,
     553        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
     554        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
     555        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
     556        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
     557        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     558        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     559        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     560        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     561        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     562        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
     563        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     564        exp.LogProperty: exp.Properties.Location.POST_NAME,
     565        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     566        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     567        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     568        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
     569        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     570        exp.Order: exp.Properties.Location.POST_SCHEMA,
     571        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
     572        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     573        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
     574        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     575        exp.Property: exp.Properties.Location.POST_WITH,
     576        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
     577        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     578        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     579        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     580        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     581        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
     582        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     583        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
     584        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
     585        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     586        exp.Set: exp.Properties.Location.POST_SCHEMA,
     587        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     588        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     589        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
     590        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
     591        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
     592        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     593        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
     594        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     595        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     596        exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
     597        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
     598        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
     599        exp.Tags: exp.Properties.Location.POST_WITH,
     600        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     601        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
     602        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     603        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
     604        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     605        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
     606        exp.UsingTemplateProperty: exp.Properties.Location.POST_SCHEMA,
     607        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
     608        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     609        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     610        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     611        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
     612        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
     613        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
     614        exp.ForceProperty: exp.Properties.Location.POST_CREATE,
     615    }
     616
     617    # Keywords that can't be used as unquoted identifier names
     618    RESERVED_KEYWORDS: t.Set[str] = set()
     619
     620    # Expressions whose comments are separated from them for better formatting
     621    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     622        exp.Command,
     623        exp.Create,
     624        exp.Describe,
     625        exp.Delete,
     626        exp.Drop,
     627        exp.From,
     628        exp.Insert,
     629        exp.Join,
     630        exp.MultitableInserts,
     631        exp.Select,
     632        exp.SetOperation,
     633        exp.Update,
     634        exp.Where,
     635        exp.With,
     636    )
     637
     638    # Expressions that should not have their comments generated in maybe_comment
     639    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     640        exp.Binary,
     641        exp.SetOperation,
     642    )
     643
     644    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
     645    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
     646        exp.Column,
     647        exp.Literal,
     648        exp.Neg,
     649        exp.Paren,
     650    )
     651
     652    PARAMETERIZABLE_TEXT_TYPES = {
     653        exp.DataType.Type.NVARCHAR,
     654        exp.DataType.Type.VARCHAR,
     655        exp.DataType.Type.CHAR,
     656        exp.DataType.Type.NCHAR,
     657    }
     658
     659    # Expressions that need to have all CTEs under them bubbled up to them
     660    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
     661
     662    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     663
     664    __slots__ = (
     665        "pretty",
     666        "identify",
     667        "normalize",
     668        "pad",
     669        "_indent",
     670        "normalize_functions",
     671        "unsupported_level",
     672        "max_unsupported",
     673        "leading_comma",
     674        "max_text_width",
     675        "comments",
     676        "dialect",
     677        "unsupported_messages",
     678        "_escaped_quote_end",
     679        "_escaped_identifier_end",
     680        "_next_name",
     681        "_identifier_start",
     682        "_identifier_end",
     683        "_quote_json_path_key_using_brackets",
     684    )
     685
     686    def __init__(
     687        self,
     688        pretty: t.Optional[bool] = None,
     689        identify: str | bool = False,
     690        normalize: bool = False,
     691        pad: int = 2,
     692        indent: int = 2,
     693        normalize_functions: t.Optional[str | bool] = None,
     694        unsupported_level: ErrorLevel = ErrorLevel.WARN,
     695        max_unsupported: int = 3,
     696        leading_comma: bool = False,
     697        max_text_width: int = 80,
     698        comments: bool = True,
     699        dialect: DialectType = None,
     700    ):
     701        import sqlglot
     702        from sqlglot.dialects import Dialect
     703
     704        self.pretty = pretty if pretty is not None else sqlglot.pretty
     705        self.identify = identify
     706        self.normalize = normalize
     707        self.pad = pad
     708        self._indent = indent
     709        self.unsupported_level = unsupported_level
     710        self.max_unsupported = max_unsupported
     711        self.leading_comma = leading_comma
     712        self.max_text_width = max_text_width
     713        self.comments = comments
     714        self.dialect = Dialect.get_or_raise(dialect)
     715
     716        # This is both a Dialect property and a Generator argument, so we prioritize the latter
     717        self.normalize_functions = (
     718            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
     719        )
     720
     721        self.unsupported_messages: t.List[str] = []
     722        self._escaped_quote_end: str = (
     723            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
     724        )
     725        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
     726
     727        self._next_name = name_sequence("_t")
     728
     729        self._identifier_start = self.dialect.IDENTIFIER_START
     730        self._identifier_end = self.dialect.IDENTIFIER_END
     731
     732        self._quote_json_path_key_using_brackets = True
     733
     734    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
     735        """
     736        Generates the SQL string corresponding to the given syntax tree.
     737
     738        Args:
     739            expression: The syntax tree.
     740            copy: Whether to copy the expression. The generator performs mutations so
     741                it is safer to copy.
     742
     743        Returns:
     744            The SQL string corresponding to `expression`.
     745        """
     746        if copy:
     747            expression = expression.copy()
     748
     749        expression = self.preprocess(expression)
     750
     751        self.unsupported_messages = []
     752        sql = self.sql(expression).strip()
     753
     754        if self.pretty:
     755            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     756
     757        if self.unsupported_level == ErrorLevel.IGNORE:
     758            return sql
     759
     760        if self.unsupported_level == ErrorLevel.WARN:
     761            for msg in self.unsupported_messages:
     762                logger.warning(msg)
     763        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     764            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     765
     766        return sql
     767
     768    def preprocess(self, expression: exp.Expression) -> exp.Expression:
     769        """Apply generic preprocessing transformations to a given expression."""
     770        expression = self._move_ctes_to_top_level(expression)
     771
     772        if self.ENSURE_BOOLS:
     773            from sqlglot.transforms import ensure_bools
     774
     775            expression = ensure_bools(expression)
     776
     777        return expression
     778
     779    def _move_ctes_to_top_level(self, expression: E) -> E:
     780        if (
     781            not expression.parent
     782            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
     783            and any(node.parent is not expression for node in expression.find_all(exp.With))
     784        ):
     785            from sqlglot.transforms import move_ctes_to_top_level
     786
     787            expression = move_ctes_to_top_level(expression)
     788        return expression
     789
     790    def unsupported(self, message: str) -> None:
     791        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     792            raise UnsupportedError(message)
     793        self.unsupported_messages.append(message)
     794
     795    def sep(self, sep: str = " ") -> str:
     796        return f"{sep.strip()}\n" if self.pretty else sep
     797
     798    def seg(self, sql: str, sep: str = " ") -> str:
     799        return f"{self.sep(sep)}{sql}"
     800
     801    def pad_comment(self, comment: str) -> str:
     802        comment = " " + comment if comment[0].strip() else comment
     803        comment = comment + " " if comment[-1].strip() else comment
     804        return comment
     805
     806    def maybe_comment(
     807        self,
     808        sql: str,
     809        expression: t.Optional[exp.Expression] = None,
     810        comments: t.Optional[t.List[str]] = None,
     811        separated: bool = False,
     812    ) -> str:
     813        comments = (
     814            ((expression and expression.comments) if comments is None else comments)  # type: ignore
     815            if self.comments
     816            else None
     817        )
     818
     819        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
     820            return sql
     821
     822        comments_sql = " ".join(
     823            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     824        )
     825
     826        if not comments_sql:
     827            return sql
     828
     829        comments_sql = self._replace_line_breaks(comments_sql)
     830
     831        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     832            return (
     833                f"{self.sep()}{comments_sql}{sql}"
     834                if not sql or sql[0].isspace()
     835                else f"{comments_sql}{self.sep()}{sql}"
     836            )
     837
     838        return f"{sql} {comments_sql}"
     839
     840    def wrap(self, expression: exp.Expression | str) -> str:
     841        this_sql = (
     842            self.sql(expression)
     843            if isinstance(expression, exp.UNWRAPPED_QUERIES)
     844            else self.sql(expression, "this")
     845        )
     846        if not this_sql:
     847            return "()"
     848
     849        this_sql = self.indent(this_sql, level=1, pad=0)
     850        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     851
     852    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     853        original = self.identify
     854        self.identify = False
     855        result = func(*args, **kwargs)
     856        self.identify = original
     857        return result
     858
     859    def normalize_func(self, name: str) -> str:
     860        if self.normalize_functions == "upper" or self.normalize_functions is True:
     861            return name.upper()
     862        if self.normalize_functions == "lower":
     863            return name.lower()
     864        return name
     865
     866    def indent(
     867        self,
     868        sql: str,
     869        level: int = 0,
     870        pad: t.Optional[int] = None,
     871        skip_first: bool = False,
     872        skip_last: bool = False,
     873    ) -> str:
     874        if not self.pretty or not sql:
     875            return sql
     876
     877        pad = self.pad if pad is None else pad
     878        lines = sql.split("\n")
     879
     880        return "\n".join(
     881            (
     882                line
     883                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     884                else f"{' ' * (level * self._indent + pad)}{line}"
     885            )
     886            for i, line in enumerate(lines)
     887        )
     888
     889    def sql(
     890        self,
     891        expression: t.Optional[str | exp.Expression],
     892        key: t.Optional[str] = None,
     893        comment: bool = True,
     894    ) -> str:
     895        if not expression:
     896            return ""
     897
     898        if isinstance(expression, str):
     899            return expression
     900
     901        if key:
     902            value = expression.args.get(key)
     903            if value:
     904                return self.sql(value)
     905            return ""
     906
     907        transform = self.TRANSFORMS.get(expression.__class__)
     908
     909        if callable(transform):
     910            sql = transform(self, expression)
     911        elif isinstance(expression, exp.Expression):
     912            exp_handler_name = f"{expression.key}_sql"
     913
     914            if hasattr(self, exp_handler_name):
     915                sql = getattr(self, exp_handler_name)(expression)
     916            elif isinstance(expression, exp.Func):
     917                sql = self.function_fallback_sql(expression)
     918            elif isinstance(expression, exp.Property):
     919                sql = self.property_sql(expression)
     920            else:
     921                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     922        else:
     923            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     924
     925        return self.maybe_comment(sql, expression) if self.comments and comment else sql
     926
     927    def uncache_sql(self, expression: exp.Uncache) -> str:
     928        table = self.sql(expression, "this")
     929        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     930        return f"UNCACHE TABLE{exists_sql} {table}"
     931
     932    def cache_sql(self, expression: exp.Cache) -> str:
     933        lazy = " LAZY" if expression.args.get("lazy") else ""
     934        table = self.sql(expression, "this")
     935        options = expression.args.get("options")
     936        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     937        sql = self.sql(expression, "expression")
     938        sql = f" AS{self.sep()}{sql}" if sql else ""
     939        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     940        return self.prepend_ctes(expression, sql)
     941
     942    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     943        if isinstance(expression.parent, exp.Cast):
     944            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     945        default = "DEFAULT " if expression.args.get("default") else ""
     946        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     947
     948    def column_parts(self, expression: exp.Column) -> str:
     949        return ".".join(
     950            self.sql(part)
     951            for part in (
     952                expression.args.get("catalog"),
     953                expression.args.get("db"),
     954                expression.args.get("table"),
     955                expression.args.get("this"),
     956            )
     957            if part
     958        )
     959
     960    def column_sql(self, expression: exp.Column) -> str:
     961        join_mark = " (+)" if expression.args.get("join_mark") else ""
     962
     963        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
     964            join_mark = ""
     965            self.unsupported("Outer join syntax using the (+) operator is not supported.")
     966
     967        return f"{self.column_parts(expression)}{join_mark}"
     968
     969    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     970        this = self.sql(expression, "this")
     971        this = f" {this}" if this else ""
     972        position = self.sql(expression, "position")
     973        return f"{position}{this}"
     974
     975    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     976        column = self.sql(expression, "this")
     977        kind = self.sql(expression, "kind")
     978        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     979        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     980        kind = f"{sep}{kind}" if kind else ""
     981        constraints = f" {constraints}" if constraints else ""
     982        position = self.sql(expression, "position")
     983        position = f" {position}" if position else ""
     984
     985        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
     986            kind = ""
     987
     988        return f"{exists}{column}{kind}{constraints}{position}"
     989
     990    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     991        this = self.sql(expression, "this")
     992        kind_sql = self.sql(expression, "kind").strip()
     993        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     994
     995    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     996        this = self.sql(expression, "this")
     997        if expression.args.get("not_null"):
     998            persisted = " PERSISTED NOT NULL"
     999        elif expression.args.get("persisted"):
    1000            persisted = " PERSISTED"
    1001        else:
    1002            persisted = ""
    1003        return f"AS {this}{persisted}"
    1004
    1005    def autoincrementcolumnconstraint_sql(self, _) -> str:
    1006        return self.token_sql(TokenType.AUTO_INCREMENT)
    1007
    1008    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    1009        if isinstance(expression.this, list):
    1010            this = self.wrap(self.expressions(expression, key="this", flat=True))
    1011        else:
    1012            this = self.sql(expression, "this")
    1013
    1014        return f"COMPRESS {this}"
    1015
    1016    def generatedasidentitycolumnconstraint_sql(
    1017        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1018    ) -> str:
    1019        this = ""
    1020        if expression.this is not None:
    1021            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1022            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1023
    1024        start = expression.args.get("start")
    1025        start = f"START WITH {start}" if start else ""
    1026        increment = expression.args.get("increment")
    1027        increment = f" INCREMENT BY {increment}" if increment else ""
    1028        minvalue = expression.args.get("minvalue")
    1029        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1030        maxvalue = expression.args.get("maxvalue")
    1031        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1032        cycle = expression.args.get("cycle")
    1033        cycle_sql = ""
    1034
    1035        if cycle is not None:
    1036            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1037            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1038
    1039        sequence_opts = ""
    1040        if start or increment or cycle_sql:
    1041            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1042            sequence_opts = f" ({sequence_opts.strip()})"
    1043
    1044        expr = self.sql(expression, "expression")
    1045        expr = f"({expr})" if expr else "IDENTITY"
    1046
    1047        return f"GENERATED{this} AS {expr}{sequence_opts}"
    1048
    1049    def generatedasrowcolumnconstraint_sql(
    1050        self, expression: exp.GeneratedAsRowColumnConstraint
    1051    ) -> str:
    1052        start = "START" if expression.args.get("start") else "END"
    1053        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1054        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
    1055
    1056    def periodforsystemtimeconstraint_sql(
    1057        self, expression: exp.PeriodForSystemTimeConstraint
    1058    ) -> str:
    1059        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
    1060
    1061    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
    1062        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
    1063
    1064    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
    1065        return f"AS {self.sql(expression, 'this')}"
    1066
    1067    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1068        desc = expression.args.get("desc")
    1069        if desc is not None:
    1070            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1071        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1072        options = f" {options}" if options else ""
    1073        return f"PRIMARY KEY{options}"
    1074
    1075    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1076        this = self.sql(expression, "this")
    1077        this = f" {this}" if this else ""
    1078        index_type = expression.args.get("index_type")
    1079        index_type = f" USING {index_type}" if index_type else ""
    1080        on_conflict = self.sql(expression, "on_conflict")
    1081        on_conflict = f" {on_conflict}" if on_conflict else ""
    1082        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1083        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1084        options = f" {options}" if options else ""
    1085        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
    1086
    1087    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
    1088        return self.sql(expression, "this")
    1089
    1090    def create_sql(self, expression: exp.Create) -> str:
    1091        kind = self.sql(expression, "kind")
    1092        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1093        properties = expression.args.get("properties")
    1094        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1095
    1096        this = self.createable_sql(expression, properties_locs)
    1097
    1098        properties_sql = ""
    1099        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1100            exp.Properties.Location.POST_WITH
    1101        ):
    1102            properties_sql = self.sql(
    1103                exp.Properties(
    1104                    expressions=[
    1105                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1106                        *properties_locs[exp.Properties.Location.POST_WITH],
    1107                    ]
    1108                )
    1109            )
    1110
    1111            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1112                properties_sql = self.sep() + properties_sql
    1113            elif not self.pretty:
    1114                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1115                properties_sql = f" {properties_sql}"
    1116
    1117        begin = " BEGIN" if expression.args.get("begin") else ""
    1118        end = " END" if expression.args.get("end") else ""
    1119
    1120        expression_sql = self.sql(expression, "expression")
    1121        if expression_sql:
    1122            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1123
    1124            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1125                postalias_props_sql = ""
    1126                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1127                    postalias_props_sql = self.properties(
    1128                        exp.Properties(
    1129                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1130                        ),
    1131                        wrapped=False,
    1132                    )
    1133                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1134                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1135
    1136        postindex_props_sql = ""
    1137        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1138            postindex_props_sql = self.properties(
    1139                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1140                wrapped=False,
    1141                prefix=" ",
    1142            )
    1143
    1144        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1145        indexes = f" {indexes}" if indexes else ""
    1146        index_sql = indexes + postindex_props_sql
    1147
    1148        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1149        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1150        unique = " UNIQUE" if expression.args.get("unique") else ""
    1151
    1152        clustered = expression.args.get("clustered")
    1153        if clustered is None:
    1154            clustered_sql = ""
    1155        elif clustered:
    1156            clustered_sql = " CLUSTERED COLUMNSTORE"
    1157        else:
    1158            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1159
    1160        postcreate_props_sql = ""
    1161        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1162            postcreate_props_sql = self.properties(
    1163                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1164                sep=" ",
    1165                prefix=" ",
    1166                wrapped=False,
    1167            )
    1168
    1169        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1170
    1171        postexpression_props_sql = ""
    1172        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1173            postexpression_props_sql = self.properties(
    1174                exp.Properties(
    1175                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1176                ),
    1177                sep=" ",
    1178                prefix=" ",
    1179                wrapped=False,
    1180            )
    1181
    1182        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1183        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1184        no_schema_binding = (
    1185            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1186        )
    1187
    1188        clone = self.sql(expression, "clone")
    1189        clone = f" {clone}" if clone else ""
    1190
    1191        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1192            properties_expression = f"{expression_sql}{properties_sql}"
    1193        else:
    1194            properties_expression = f"{properties_sql}{expression_sql}"
    1195
    1196        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1197        return self.prepend_ctes(expression, expression_sql)
    1198
    1199    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1200        start = self.sql(expression, "start")
    1201        start = f"START WITH {start}" if start else ""
    1202        increment = self.sql(expression, "increment")
    1203        increment = f" INCREMENT BY {increment}" if increment else ""
    1204        minvalue = self.sql(expression, "minvalue")
    1205        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1206        maxvalue = self.sql(expression, "maxvalue")
    1207        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1208        owned = self.sql(expression, "owned")
    1209        owned = f" OWNED BY {owned}" if owned else ""
    1210
    1211        cache = expression.args.get("cache")
    1212        if cache is None:
    1213            cache_str = ""
    1214        elif cache is True:
    1215            cache_str = " CACHE"
    1216        else:
    1217            cache_str = f" CACHE {cache}"
    1218
    1219        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1220        options = f" {options}" if options else ""
    1221
    1222        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
    1223
    1224    def clone_sql(self, expression: exp.Clone) -> str:
    1225        this = self.sql(expression, "this")
    1226        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1227        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1228        return f"{shallow}{keyword} {this}"
    1229
    1230    def describe_sql(self, expression: exp.Describe) -> str:
    1231        style = expression.args.get("style")
    1232        style = f" {style}" if style else ""
    1233        partition = self.sql(expression, "partition")
    1234        partition = f" {partition}" if partition else ""
    1235        format = self.sql(expression, "format")
    1236        format = f" {format}" if format else ""
    1237
    1238        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
    1239
    1240    def heredoc_sql(self, expression: exp.Heredoc) -> str:
    1241        tag = self.sql(expression, "tag")
    1242        return f"${tag}${self.sql(expression, 'this')}${tag}$"
    1243
    1244    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1245        with_ = self.sql(expression, "with")
    1246        if with_:
    1247            sql = f"{with_}{self.sep()}{sql}"
    1248        return sql
    1249
    1250    def with_sql(self, expression: exp.With) -> str:
    1251        sql = self.expressions(expression, flat=True)
    1252        recursive = (
    1253            "RECURSIVE "
    1254            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1255            else ""
    1256        )
    1257        search = self.sql(expression, "search")
    1258        search = f" {search}" if search else ""
    1259
    1260        return f"WITH {recursive}{sql}{search}"
    1261
    1262    def cte_sql(self, expression: exp.CTE) -> str:
    1263        alias = expression.args.get("alias")
    1264        if alias:
    1265            alias.add_comments(expression.pop_comments())
    1266
    1267        alias_sql = self.sql(expression, "alias")
    1268
    1269        materialized = expression.args.get("materialized")
    1270        if materialized is False:
    1271            materialized = "NOT MATERIALIZED "
    1272        elif materialized:
    1273            materialized = "MATERIALIZED "
    1274
    1275        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
    1276
    1277    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1278        alias = self.sql(expression, "this")
    1279        columns = self.expressions(expression, key="columns", flat=True)
    1280        columns = f"({columns})" if columns else ""
    1281
    1282        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1283            columns = ""
    1284            self.unsupported("Named columns are not supported in table alias.")
    1285
    1286        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1287            alias = self._next_name()
    1288
    1289        return f"{alias}{columns}"
    1290
    1291    def bitstring_sql(self, expression: exp.BitString) -> str:
    1292        this = self.sql(expression, "this")
    1293        if self.dialect.BIT_START:
    1294            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1295        return f"{int(this, 2)}"
    1296
    1297    def hexstring_sql(
    1298        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
    1299    ) -> str:
    1300        this = self.sql(expression, "this")
    1301        is_integer_type = expression.args.get("is_integer")
    1302
    1303        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
    1304            not self.dialect.HEX_START and not binary_function_repr
    1305        ):
    1306            # Integer representation will be returned if:
    1307            # - The read dialect treats the hex value as integer literal but not the write
    1308            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
    1309            return f"{int(this, 16)}"
    1310
    1311        if not is_integer_type:
    1312            # Read dialect treats the hex value as BINARY/BLOB
    1313            if binary_function_repr:
    1314                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
    1315                return self.func(binary_function_repr, exp.Literal.string(this))
    1316            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
    1317                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
    1318                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
    1319
    1320        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1321
    1322    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1323        this = self.sql(expression, "this")
    1324        if self.dialect.BYTE_START:
    1325            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1326        return this
    1327
    1328    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1329        this = self.sql(expression, "this")
    1330        escape = expression.args.get("escape")
    1331
    1332        if self.dialect.UNICODE_START:
    1333            escape_substitute = r"\\\1"
    1334            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1335        else:
    1336            escape_substitute = r"\\u\1"
    1337            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1338
    1339        if escape:
    1340            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1341            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1342        else:
    1343            escape_pattern = ESCAPED_UNICODE_RE
    1344            escape_sql = ""
    1345
    1346        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1347            this = escape_pattern.sub(escape_substitute, this)
    1348
    1349        return f"{left_quote}{this}{right_quote}{escape_sql}"
    1350
    1351    def rawstring_sql(self, expression: exp.RawString) -> str:
    1352        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
    1353        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
    1354
    1355    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1356        this = self.sql(expression, "this")
    1357        specifier = self.sql(expression, "expression")
    1358        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1359        return f"{this}{specifier}"
    1360
    1361    def datatype_sql(self, expression: exp.DataType) -> str:
    1362        nested = ""
    1363        values = ""
    1364        interior = self.expressions(expression, flat=True)
    1365
    1366        type_value = expression.this
    1367        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1368            type_sql = self.sql(expression, "kind")
    1369        else:
    1370            type_sql = (
    1371                self.TYPE_MAPPING.get(type_value, type_value.value)
    1372                if isinstance(type_value, exp.DataType.Type)
    1373                else type_value
    1374            )
    1375
    1376        if interior:
    1377            if expression.args.get("nested"):
    1378                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1379                if expression.args.get("values") is not None:
    1380                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1381                    values = self.expressions(expression, key="values", flat=True)
    1382                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1383            elif type_value == exp.DataType.Type.INTERVAL:
    1384                nested = f" {interior}"
    1385            else:
    1386                nested = f"({interior})"
    1387
    1388        type_sql = f"{type_sql}{nested}{values}"
    1389        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1390            exp.DataType.Type.TIMETZ,
    1391            exp.DataType.Type.TIMESTAMPTZ,
    1392        ):
    1393            type_sql = f"{type_sql} WITH TIME ZONE"
    1394
    1395        return type_sql
    1396
    1397    def directory_sql(self, expression: exp.Directory) -> str:
    1398        local = "LOCAL " if expression.args.get("local") else ""
    1399        row_format = self.sql(expression, "row_format")
    1400        row_format = f" {row_format}" if row_format else ""
    1401        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
    1402
    1403    def delete_sql(self, expression: exp.Delete) -> str:
    1404        this = self.sql(expression, "this")
    1405        this = f" FROM {this}" if this else ""
    1406        using = self.sql(expression, "using")
    1407        using = f" USING {using}" if using else ""
    1408        cluster = self.sql(expression, "cluster")
    1409        cluster = f" {cluster}" if cluster else ""
    1410        where = self.sql(expression, "where")
    1411        returning = self.sql(expression, "returning")
    1412        limit = self.sql(expression, "limit")
    1413        tables = self.expressions(expression, key="tables")
    1414        tables = f" {tables}" if tables else ""
    1415        if self.RETURNING_END:
    1416            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1417        else:
    1418            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1419        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
    1420
    1421    def drop_sql(self, expression: exp.Drop) -> str:
    1422        this = self.sql(expression, "this")
    1423        expressions = self.expressions(expression, flat=True)
    1424        expressions = f" ({expressions})" if expressions else ""
    1425        kind = expression.args["kind"]
    1426        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1427        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1428        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1429        on_cluster = self.sql(expression, "cluster")
    1430        on_cluster = f" {on_cluster}" if on_cluster else ""
    1431        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1432        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1433        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1434        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1435        purge = " PURGE" if expression.args.get("purge") else ""
    1436        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
    1437
    1438    def set_operation(self, expression: exp.SetOperation) -> str:
    1439        op_type = type(expression)
    1440        op_name = op_type.key.upper()
    1441
    1442        distinct = expression.args.get("distinct")
    1443        if (
    1444            distinct is False
    1445            and op_type in (exp.Except, exp.Intersect)
    1446            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1447        ):
    1448            self.unsupported(f"{op_name} ALL is not supported")
    1449
    1450        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1451
    1452        if distinct is None:
    1453            distinct = default_distinct
    1454            if distinct is None:
    1455                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1456
    1457        if distinct is default_distinct:
    1458            distinct_or_all = ""
    1459        else:
    1460            distinct_or_all = " DISTINCT" if distinct else " ALL"
    1461
    1462        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
    1463        side_kind = f"{side_kind} " if side_kind else ""
    1464
    1465        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1466        on = self.expressions(expression, key="on", flat=True)
    1467        on = f" ON ({on})" if on else ""
    1468
    1469        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
    1470
    1471    def set_operations(self, expression: exp.SetOperation) -> str:
    1472        if not self.SET_OP_MODIFIERS:
    1473            limit = expression.args.get("limit")
    1474            order = expression.args.get("order")
    1475
    1476            if limit or order:
    1477                select = self._move_ctes_to_top_level(
    1478                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1479                )
    1480
    1481                if limit:
    1482                    select = select.limit(limit.pop(), copy=False)
    1483                if order:
    1484                    select = select.order_by(order.pop(), copy=False)
    1485                return self.sql(select)
    1486
    1487        sqls: t.List[str] = []
    1488        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1489
    1490        while stack:
    1491            node = stack.pop()
    1492
    1493            if isinstance(node, exp.SetOperation):
    1494                stack.append(node.expression)
    1495                stack.append(
    1496                    self.maybe_comment(
    1497                        self.set_operation(node), comments=node.comments, separated=True
    1498                    )
    1499                )
    1500                stack.append(node.this)
    1501            else:
    1502                sqls.append(self.sql(node))
    1503
    1504        this = self.sep().join(sqls)
    1505        this = self.query_modifiers(expression, this)
    1506        return self.prepend_ctes(expression, this)
    1507
    1508    def fetch_sql(self, expression: exp.Fetch) -> str:
    1509        direction = expression.args.get("direction")
    1510        direction = f" {direction}" if direction else ""
    1511        count = self.sql(expression, "count")
    1512        count = f" {count}" if count else ""
    1513        limit_options = self.sql(expression, "limit_options")
    1514        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
    1515        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
    1516
    1517    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
    1518        percent = " PERCENT" if expression.args.get("percent") else ""
    1519        rows = " ROWS" if expression.args.get("rows") else ""
    1520        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
    1521        if not with_ties and rows:
    1522            with_ties = " ONLY"
    1523        return f"{percent}{rows}{with_ties}"
    1524
    1525    def filter_sql(self, expression: exp.Filter) -> str:
    1526        if self.AGGREGATE_FILTER_SUPPORTED:
    1527            this = self.sql(expression, "this")
    1528            where = self.sql(expression, "expression").strip()
    1529            return f"{this} FILTER({where})"
    1530
    1531        agg = expression.this
    1532        agg_arg = agg.this
    1533        cond = expression.expression.this
    1534        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1535        return self.sql(agg)
    1536
    1537    def hint_sql(self, expression: exp.Hint) -> str:
    1538        if not self.QUERY_HINTS:
    1539            self.unsupported("Hints are not supported")
    1540            return ""
    1541
    1542        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
    1543
    1544    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1545        using = self.sql(expression, "using")
    1546        using = f" USING {using}" if using else ""
    1547        columns = self.expressions(expression, key="columns", flat=True)
    1548        columns = f"({columns})" if columns else ""
    1549        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1550        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1551        where = self.sql(expression, "where")
    1552        include = self.expressions(expression, key="include", flat=True)
    1553        if include:
    1554            include = f" INCLUDE ({include})"
    1555        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1556        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1557        tablespace = self.sql(expression, "tablespace")
    1558        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1559        on = self.sql(expression, "on")
    1560        on = f" ON {on}" if on else ""
    1561
    1562        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
    1563
    1564    def index_sql(self, expression: exp.Index) -> str:
    1565        unique = "UNIQUE " if expression.args.get("unique") else ""
    1566        primary = "PRIMARY " if expression.args.get("primary") else ""
    1567        amp = "AMP " if expression.args.get("amp") else ""
    1568        name = self.sql(expression, "this")
    1569        name = f"{name} " if name else ""
    1570        table = self.sql(expression, "table")
    1571        table = f"{self.INDEX_ON} {table}" if table else ""
    1572
    1573        index = "INDEX " if not table else ""
    1574
    1575        params = self.sql(expression, "params")
    1576        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
    1577
    1578    def identifier_sql(self, expression: exp.Identifier) -> str:
    1579        text = expression.name
    1580        lower = text.lower()
    1581        text = lower if self.normalize and not expression.quoted else text
    1582        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1583        if (
    1584            expression.quoted
    1585            or self.dialect.can_identify(text, self.identify)
    1586            or lower in self.RESERVED_KEYWORDS
    1587            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1588        ):
    1589            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1590        return text
    1591
    1592    def hex_sql(self, expression: exp.Hex) -> str:
    1593        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1594        if self.dialect.HEX_LOWERCASE:
    1595            text = self.func("LOWER", text)
    1596
    1597        return text
    1598
    1599    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1600        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1601        if not self.dialect.HEX_LOWERCASE:
    1602            text = self.func("LOWER", text)
    1603        return text
    1604
    1605    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1606        input_format = self.sql(expression, "input_format")
    1607        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1608        output_format = self.sql(expression, "output_format")
    1609        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1610        return self.sep().join((input_format, output_format))
    1611
    1612    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1613        string = self.sql(exp.Literal.string(expression.name))
    1614        return f"{prefix}{string}"
    1615
    1616    def partition_sql(self, expression: exp.Partition) -> str:
    1617        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
    1618        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
    1619
    1620    def properties_sql(self, expression: exp.Properties) -> str:
    1621        root_properties = []
    1622        with_properties = []
    1623
    1624        for p in expression.expressions:
    1625            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1626            if p_loc == exp.Properties.Location.POST_WITH:
    1627                with_properties.append(p)
    1628            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1629                root_properties.append(p)
    1630
    1631        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1632        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1633
    1634        if root_props and with_props and not self.pretty:
    1635            with_props = " " + with_props
    1636
    1637        return root_props + with_props
    1638
    1639    def root_properties(self, properties: exp.Properties) -> str:
    1640        if properties.expressions:
    1641            return self.expressions(properties, indent=False, sep=" ")
    1642        return ""
    1643
    1644    def properties(
    1645        self,
    1646        properties: exp.Properties,
    1647        prefix: str = "",
    1648        sep: str = ", ",
    1649        suffix: str = "",
    1650        wrapped: bool = True,
    1651    ) -> str:
    1652        if properties.expressions:
    1653            expressions = self.expressions(properties, sep=sep, indent=False)
    1654            if expressions:
    1655                expressions = self.wrap(expressions) if wrapped else expressions
    1656                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1657        return ""
    1658
    1659    def with_properties(self, properties: exp.Properties) -> str:
    1660        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
    1661
    1662    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1663        properties_locs = defaultdict(list)
    1664        for p in properties.expressions:
    1665            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1666            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1667                properties_locs[p_loc].append(p)
    1668            else:
    1669                self.unsupported(f"Unsupported property {p.key}")
    1670
    1671        return properties_locs
    1672
    1673    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1674        if isinstance(expression.this, exp.Dot):
    1675            return self.sql(expression, "this")
    1676        return f"'{expression.name}'" if string_key else expression.name
    1677
    1678    def property_sql(self, expression: exp.Property) -> str:
    1679        property_cls = expression.__class__
    1680        if property_cls == exp.Property:
    1681            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1682
    1683        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1684        if not property_name:
    1685            self.unsupported(f"Unsupported property {expression.key}")
    1686
    1687        return f"{property_name}={self.sql(expression, 'this')}"
    1688
    1689    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1690        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1691            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1692            options = f" {options}" if options else ""
    1693
    1694            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1695            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1696                like = f"({like})"
    1697
    1698            return like
    1699
    1700        if expression.expressions:
    1701            self.unsupported("Transpilation of LIKE property options is unsupported")
    1702
    1703        select = exp.select("*").from_(expression.this).limit(0)
    1704        return f"AS {self.sql(select)}"
    1705
    1706    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1707        no = "NO " if expression.args.get("no") else ""
    1708        protection = " PROTECTION" if expression.args.get("protection") else ""
    1709        return f"{no}FALLBACK{protection}"
    1710
    1711    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1712        no = "NO " if expression.args.get("no") else ""
    1713        local = expression.args.get("local")
    1714        local = f"{local} " if local else ""
    1715        dual = "DUAL " if expression.args.get("dual") else ""
    1716        before = "BEFORE " if expression.args.get("before") else ""
    1717        after = "AFTER " if expression.args.get("after") else ""
    1718        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1719
    1720    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1721        freespace = self.sql(expression, "this")
    1722        percent = " PERCENT" if expression.args.get("percent") else ""
    1723        return f"FREESPACE={freespace}{percent}"
    1724
    1725    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1726        if expression.args.get("default"):
    1727            property = "DEFAULT"
    1728        elif expression.args.get("on"):
    1729            property = "ON"
    1730        else:
    1731            property = "OFF"
    1732        return f"CHECKSUM={property}"
    1733
    1734    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1735        if expression.args.get("no"):
    1736            return "NO MERGEBLOCKRATIO"
    1737        if expression.args.get("default"):
    1738            return "DEFAULT MERGEBLOCKRATIO"
    1739
    1740        percent = " PERCENT" if expression.args.get("percent") else ""
    1741        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1742
    1743    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1744        default = expression.args.get("default")
    1745        minimum = expression.args.get("minimum")
    1746        maximum = expression.args.get("maximum")
    1747        if default or minimum or maximum:
    1748            if default:
    1749                prop = "DEFAULT"
    1750            elif minimum:
    1751                prop = "MINIMUM"
    1752            else:
    1753                prop = "MAXIMUM"
    1754            return f"{prop} DATABLOCKSIZE"
    1755        units = expression.args.get("units")
    1756        units = f" {units}" if units else ""
    1757        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1758
    1759    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1760        autotemp = expression.args.get("autotemp")
    1761        always = expression.args.get("always")
    1762        default = expression.args.get("default")
    1763        manual = expression.args.get("manual")
    1764        never = expression.args.get("never")
    1765
    1766        if autotemp is not None:
    1767            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1768        elif always:
    1769            prop = "ALWAYS"
    1770        elif default:
    1771            prop = "DEFAULT"
    1772        elif manual:
    1773            prop = "MANUAL"
    1774        elif never:
    1775            prop = "NEVER"
    1776        return f"BLOCKCOMPRESSION={prop}"
    1777
    1778    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1779        no = expression.args.get("no")
    1780        no = " NO" if no else ""
    1781        concurrent = expression.args.get("concurrent")
    1782        concurrent = " CONCURRENT" if concurrent else ""
    1783        target = self.sql(expression, "target")
    1784        target = f" {target}" if target else ""
    1785        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
    1786
    1787    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1788        if isinstance(expression.this, list):
    1789            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1790        if expression.this:
    1791            modulus = self.sql(expression, "this")
    1792            remainder = self.sql(expression, "expression")
    1793            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1794
    1795        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1796        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1797        return f"FROM ({from_expressions}) TO ({to_expressions})"
    1798
    1799    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1800        this = self.sql(expression, "this")
    1801
    1802        for_values_or_default = expression.expression
    1803        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1804            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1805        else:
    1806            for_values_or_default = " DEFAULT"
    1807
    1808        return f"PARTITION OF {this}{for_values_or_default}"
    1809
    1810    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1811        kind = expression.args.get("kind")
    1812        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1813        for_or_in = expression.args.get("for_or_in")
    1814        for_or_in = f" {for_or_in}" if for_or_in else ""
    1815        lock_type = expression.args.get("lock_type")
    1816        override = " OVERRIDE" if expression.args.get("override") else ""
    1817        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
    1818
    1819    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1820        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1821        statistics = expression.args.get("statistics")
    1822        statistics_sql = ""
    1823        if statistics is not None:
    1824            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1825        return f"{data_sql}{statistics_sql}"
    1826
    1827    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1828        this = self.sql(expression, "this")
    1829        this = f"HISTORY_TABLE={this}" if this else ""
    1830        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1831        data_consistency = (
    1832            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1833        )
    1834        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1835        retention_period = (
    1836            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1837        )
    1838
    1839        if this:
    1840            on_sql = self.func("ON", this, data_consistency, retention_period)
    1841        else:
    1842            on_sql = "ON" if expression.args.get("on") else "OFF"
    1843
    1844        sql = f"SYSTEM_VERSIONING={on_sql}"
    1845
    1846        return f"WITH({sql})" if expression.args.get("with") else sql
    1847
    1848    def insert_sql(self, expression: exp.Insert) -> str:
    1849        hint = self.sql(expression, "hint")
    1850        overwrite = expression.args.get("overwrite")
    1851
    1852        if isinstance(expression.this, exp.Directory):
    1853            this = " OVERWRITE" if overwrite else " INTO"
    1854        else:
    1855            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1856
    1857        stored = self.sql(expression, "stored")
    1858        stored = f" {stored}" if stored else ""
    1859        alternative = expression.args.get("alternative")
    1860        alternative = f" OR {alternative}" if alternative else ""
    1861        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1862        is_function = expression.args.get("is_function")
    1863        if is_function:
    1864            this = f"{this} FUNCTION"
    1865        this = f"{this} {self.sql(expression, 'this')}"
    1866
    1867        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1868        where = self.sql(expression, "where")
    1869        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1870        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1871        on_conflict = self.sql(expression, "conflict")
    1872        on_conflict = f" {on_conflict}" if on_conflict else ""
    1873        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1874        returning = self.sql(expression, "returning")
    1875
    1876        if self.RETURNING_END:
    1877            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1878        else:
    1879            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1880
    1881        partition_by = self.sql(expression, "partition")
    1882        partition_by = f" {partition_by}" if partition_by else ""
    1883        settings = self.sql(expression, "settings")
    1884        settings = f" {settings}" if settings else ""
    1885
    1886        source = self.sql(expression, "source")
    1887        source = f"TABLE {source}" if source else ""
    1888
    1889        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1890        return self.prepend_ctes(expression, sql)
    1891
    1892    def introducer_sql(self, expression: exp.Introducer) -> str:
    1893        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1894
    1895    def kill_sql(self, expression: exp.Kill) -> str:
    1896        kind = self.sql(expression, "kind")
    1897        kind = f" {kind}" if kind else ""
    1898        this = self.sql(expression, "this")
    1899        this = f" {this}" if this else ""
    1900        return f"KILL{kind}{this}"
    1901
    1902    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1903        return expression.name
    1904
    1905    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1906        return expression.name
    1907
    1908    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1909        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1910
    1911        constraint = self.sql(expression, "constraint")
    1912        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1913
    1914        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1915        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1916        action = self.sql(expression, "action")
    1917
    1918        expressions = self.expressions(expression, flat=True)
    1919        if expressions:
    1920            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1921            expressions = f" {set_keyword}{expressions}"
    1922
    1923        where = self.sql(expression, "where")
    1924        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
    1925
    1926    def returning_sql(self, expression: exp.Returning) -> str:
    1927        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1928
    1929    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1930        fields = self.sql(expression, "fields")
    1931        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1932        escaped = self.sql(expression, "escaped")
    1933        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1934        items = self.sql(expression, "collection_items")
    1935        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1936        keys = self.sql(expression, "map_keys")
    1937        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1938        lines = self.sql(expression, "lines")
    1939        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1940        null = self.sql(expression, "null")
    1941        null = f" NULL DEFINED AS {null}" if null else ""
    1942        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1943
    1944    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1945        return f"WITH ({self.expressions(expression, flat=True)})"
    1946
    1947    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1948        this = f"{self.sql(expression, 'this')} INDEX"
    1949        target = self.sql(expression, "target")
    1950        target = f" FOR {target}" if target else ""
    1951        return f"{this}{target} ({self.expressions(expression, flat=True)})"
    1952
    1953    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1954        this = self.sql(expression, "this")
    1955        kind = self.sql(expression, "kind")
    1956        expr = self.sql(expression, "expression")
    1957        return f"{this} ({kind} => {expr})"
    1958
    1959    def table_parts(self, expression: exp.Table) -> str:
    1960        return ".".join(
    1961            self.sql(part)
    1962            for part in (
    1963                expression.args.get("catalog"),
    1964                expression.args.get("db"),
    1965                expression.args.get("this"),
    1966            )
    1967            if part is not None
    1968        )
    1969
    1970    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1971        table = self.table_parts(expression)
    1972        only = "ONLY " if expression.args.get("only") else ""
    1973        partition = self.sql(expression, "partition")
    1974        partition = f" {partition}" if partition else ""
    1975        version = self.sql(expression, "version")
    1976        version = f" {version}" if version else ""
    1977        alias = self.sql(expression, "alias")
    1978        alias = f"{sep}{alias}" if alias else ""
    1979
    1980        sample = self.sql(expression, "sample")
    1981        if self.dialect.ALIAS_POST_TABLESAMPLE:
    1982            sample_pre_alias = sample
    1983            sample_post_alias = ""
    1984        else:
    1985            sample_pre_alias = ""
    1986            sample_post_alias = sample
    1987
    1988        hints = self.expressions(expression, key="hints", sep=" ")
    1989        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1990        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    1991        joins = self.indent(
    1992            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1993        )
    1994        laterals = self.expressions(expression, key="laterals", sep="")
    1995
    1996        file_format = self.sql(expression, "format")
    1997        if file_format:
    1998            pattern = self.sql(expression, "pattern")
    1999            pattern = f", PATTERN => {pattern}" if pattern else ""
    2000            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    2001
    2002        ordinality = expression.args.get("ordinality") or ""
    2003        if ordinality:
    2004            ordinality = f" WITH ORDINALITY{alias}"
    2005            alias = ""
    2006
    2007        when = self.sql(expression, "when")
    2008        if when:
    2009            table = f"{table} {when}"
    2010
    2011        changes = self.sql(expression, "changes")
    2012        changes = f" {changes}" if changes else ""
    2013
    2014        rows_from = self.expressions(expression, key="rows_from")
    2015        if rows_from:
    2016            table = f"ROWS FROM {self.wrap(rows_from)}"
    2017
    2018        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
    2019
    2020    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
    2021        table = self.func("TABLE", expression.this)
    2022        alias = self.sql(expression, "alias")
    2023        alias = f" AS {alias}" if alias else ""
    2024        sample = self.sql(expression, "sample")
    2025        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2026        joins = self.indent(
    2027            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    2028        )
    2029        return f"{table}{alias}{pivots}{sample}{joins}"
    2030
    2031    def tablesample_sql(
    2032        self,
    2033        expression: exp.TableSample,
    2034        tablesample_keyword: t.Optional[str] = None,
    2035    ) -> str:
    2036        method = self.sql(expression, "method")
    2037        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    2038        numerator = self.sql(expression, "bucket_numerator")
    2039        denominator = self.sql(expression, "bucket_denominator")
    2040        field = self.sql(expression, "bucket_field")
    2041        field = f" ON {field}" if field else ""
    2042        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    2043        seed = self.sql(expression, "seed")
    2044        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    2045
    2046        size = self.sql(expression, "size")
    2047        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    2048            size = f"{size} ROWS"
    2049
    2050        percent = self.sql(expression, "percent")
    2051        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    2052            percent = f"{percent} PERCENT"
    2053
    2054        expr = f"{bucket}{percent}{size}"
    2055        if self.TABLESAMPLE_REQUIRES_PARENS:
    2056            expr = f"({expr})"
    2057
    2058        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
    2059
    2060    def pivot_sql(self, expression: exp.Pivot) -> str:
    2061        expressions = self.expressions(expression, flat=True)
    2062        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2063
    2064        group = self.sql(expression, "group")
    2065
    2066        if expression.this:
    2067            this = self.sql(expression, "this")
    2068            if not expressions:
    2069                return f"UNPIVOT {this}"
    2070
    2071            on = f"{self.seg('ON')} {expressions}"
    2072            into = self.sql(expression, "into")
    2073            into = f"{self.seg('INTO')} {into}" if into else ""
    2074            using = self.expressions(expression, key="using", flat=True)
    2075            using = f"{self.seg('USING')} {using}" if using else ""
    2076            return f"{direction} {this}{on}{into}{using}{group}"
    2077
    2078        alias = self.sql(expression, "alias")
    2079        alias = f" AS {alias}" if alias else ""
    2080
    2081        fields = self.expressions(
    2082            expression,
    2083            "fields",
    2084            sep=" ",
    2085            dynamic=True,
    2086            new_line=True,
    2087            skip_first=True,
    2088            skip_last=True,
    2089        )
    2090
    2091        include_nulls = expression.args.get("include_nulls")
    2092        if include_nulls is not None:
    2093            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2094        else:
    2095            nulls = ""
    2096
    2097        default_on_null = self.sql(expression, "default_on_null")
    2098        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2099        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
    2100
    2101    def version_sql(self, expression: exp.Version) -> str:
    2102        this = f"FOR {expression.name}"
    2103        kind = expression.text("kind")
    2104        expr = self.sql(expression, "expression")
    2105        return f"{this} {kind} {expr}"
    2106
    2107    def tuple_sql(self, expression: exp.Tuple) -> str:
    2108        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    2109
    2110    def update_sql(self, expression: exp.Update) -> str:
    2111        this = self.sql(expression, "this")
    2112        set_sql = self.expressions(expression, flat=True)
    2113        from_sql = self.sql(expression, "from")
    2114        where_sql = self.sql(expression, "where")
    2115        returning = self.sql(expression, "returning")
    2116        order = self.sql(expression, "order")
    2117        limit = self.sql(expression, "limit")
    2118        if self.RETURNING_END:
    2119            expression_sql = f"{from_sql}{where_sql}{returning}"
    2120        else:
    2121            expression_sql = f"{returning}{from_sql}{where_sql}"
    2122        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2123        return self.prepend_ctes(expression, sql)
    2124
    2125    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2126        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2127
    2128        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2129        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2130            args = self.expressions(expression)
    2131            alias = self.sql(expression, "alias")
    2132            values = f"VALUES{self.seg('')}{args}"
    2133            values = (
    2134                f"({values})"
    2135                if self.WRAP_DERIVED_VALUES
    2136                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2137                else values
    2138            )
    2139            return f"{values} AS {alias}" if alias else values
    2140
    2141        # Converts `VALUES...` expression into a series of select unions.
    2142        alias_node = expression.args.get("alias")
    2143        column_names = alias_node and alias_node.columns
    2144
    2145        selects: t.List[exp.Query] = []
    2146
    2147        for i, tup in enumerate(expression.expressions):
    2148            row = tup.expressions
    2149
    2150            if i == 0 and column_names:
    2151                row = [
    2152                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2153                ]
    2154
    2155            selects.append(exp.Select(expressions=row))
    2156
    2157        if self.pretty:
    2158            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2159            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2160            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2161            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2162            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2163
    2164        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2165        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2166        return f"({unions}){alias}"
    2167
    2168    def var_sql(self, expression: exp.Var) -> str:
    2169        return self.sql(expression, "this")
    2170
    2171    @unsupported_args("expressions")
    2172    def into_sql(self, expression: exp.Into) -> str:
    2173        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2174        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2175        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    2176
    2177    def from_sql(self, expression: exp.From) -> str:
    2178        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    2179
    2180    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
    2181        grouping_sets = self.expressions(expression, indent=False)
    2182        return f"GROUPING SETS {self.wrap(grouping_sets)}"
    2183
    2184    def rollup_sql(self, expression: exp.Rollup) -> str:
    2185        expressions = self.expressions(expression, indent=False)
    2186        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
    2187
    2188    def cube_sql(self, expression: exp.Cube) -> str:
    2189        expressions = self.expressions(expression, indent=False)
    2190        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
    2191
    2192    def group_sql(self, expression: exp.Group) -> str:
    2193        group_by_all = expression.args.get("all")
    2194        if group_by_all is True:
    2195            modifier = " ALL"
    2196        elif group_by_all is False:
    2197            modifier = " DISTINCT"
    2198        else:
    2199            modifier = ""
    2200
    2201        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2202
    2203        grouping_sets = self.expressions(expression, key="grouping_sets")
    2204        cube = self.expressions(expression, key="cube")
    2205        rollup = self.expressions(expression, key="rollup")
    2206
    2207        groupings = csv(
    2208            self.seg(grouping_sets) if grouping_sets else "",
    2209            self.seg(cube) if cube else "",
    2210            self.seg(rollup) if rollup else "",
    2211            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2212            sep=self.GROUPINGS_SEP,
    2213        )
    2214
    2215        if (
    2216            expression.expressions
    2217            and groupings
    2218            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2219        ):
    2220            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2221
    2222        return f"{group_by}{groupings}"
    2223
    2224    def having_sql(self, expression: exp.Having) -> str:
    2225        this = self.indent(self.sql(expression, "this"))
    2226        return f"{self.seg('HAVING')}{self.sep()}{this}"
    2227
    2228    def connect_sql(self, expression: exp.Connect) -> str:
    2229        start = self.sql(expression, "start")
    2230        start = self.seg(f"START WITH {start}") if start else ""
    2231        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2232        connect = self.sql(expression, "connect")
    2233        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2234        return start + connect
    2235
    2236    def prior_sql(self, expression: exp.Prior) -> str:
    2237        return f"PRIOR {self.sql(expression, 'this')}"
    2238
    2239    def join_sql(self, expression: exp.Join) -> str:
    2240        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2241            side = None
    2242        else:
    2243            side = expression.side
    2244
    2245        op_sql = " ".join(
    2246            op
    2247            for op in (
    2248                expression.method,
    2249                "GLOBAL" if expression.args.get("global") else None,
    2250                side,
    2251                expression.kind,
    2252                expression.hint if self.JOIN_HINTS else None,
    2253            )
    2254            if op
    2255        )
    2256        match_cond = self.sql(expression, "match_condition")
    2257        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2258        on_sql = self.sql(expression, "on")
    2259        using = expression.args.get("using")
    2260
    2261        if not on_sql and using:
    2262            on_sql = csv(*(self.sql(column) for column in using))
    2263
    2264        this = expression.this
    2265        this_sql = self.sql(this)
    2266
    2267        exprs = self.expressions(expression)
    2268        if exprs:
    2269            this_sql = f"{this_sql},{self.seg(exprs)}"
    2270
    2271        if on_sql:
    2272            on_sql = self.indent(on_sql, skip_first=True)
    2273            space = self.seg(" " * self.pad) if self.pretty else " "
    2274            if using:
    2275                on_sql = f"{space}USING ({on_sql})"
    2276            else:
    2277                on_sql = f"{space}ON {on_sql}"
    2278        elif not op_sql:
    2279            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2280                return f" {this_sql}"
    2281
    2282            return f", {this_sql}"
    2283
    2284        if op_sql != "STRAIGHT_JOIN":
    2285            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2286
    2287        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
    2288
    2289    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    2290        args = self.expressions(expression, flat=True)
    2291        args = f"({args})" if len(args.split(",")) > 1 else args
    2292        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    2293
    2294    def lateral_op(self, expression: exp.Lateral) -> str:
    2295        cross_apply = expression.args.get("cross_apply")
    2296
    2297        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2298        if cross_apply is True:
    2299            op = "INNER JOIN "
    2300        elif cross_apply is False:
    2301            op = "LEFT JOIN "
    2302        else:
    2303            op = ""
    2304
    2305        return f"{op}LATERAL"
    2306
    2307    def lateral_sql(self, expression: exp.Lateral) -> str:
    2308        this = self.sql(expression, "this")
    2309
    2310        if expression.args.get("view"):
    2311            alias = expression.args["alias"]
    2312            columns = self.expressions(alias, key="columns", flat=True)
    2313            table = f" {alias.name}" if alias.name else ""
    2314            columns = f" AS {columns}" if columns else ""
    2315            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2316            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2317
    2318        alias = self.sql(expression, "alias")
    2319        alias = f" AS {alias}" if alias else ""
    2320
    2321        ordinality = expression.args.get("ordinality") or ""
    2322        if ordinality:
    2323            ordinality = f" WITH ORDINALITY{alias}"
    2324            alias = ""
    2325
    2326        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
    2327
    2328    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2329        this = self.sql(expression, "this")
    2330
    2331        args = [
    2332            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2333            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2334            if e
    2335        ]
    2336
    2337        args_sql = ", ".join(self.sql(e) for e in args)
    2338        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2339        expressions = self.expressions(expression, flat=True)
    2340        limit_options = self.sql(expression, "limit_options")
    2341        expressions = f" BY {expressions}" if expressions else ""
    2342
    2343        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
    2344
    2345    def offset_sql(self, expression: exp.Offset) -> str:
    2346        this = self.sql(expression, "this")
    2347        value = expression.expression
    2348        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2349        expressions = self.expressions(expression, flat=True)
    2350        expressions = f" BY {expressions}" if expressions else ""
    2351        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
    2352
    2353    def setitem_sql(self, expression: exp.SetItem) -> str:
    2354        kind = self.sql(expression, "kind")
    2355        kind = f"{kind} " if kind else ""
    2356        this = self.sql(expression, "this")
    2357        expressions = self.expressions(expression)
    2358        collate = self.sql(expression, "collate")
    2359        collate = f" COLLATE {collate}" if collate else ""
    2360        global_ = "GLOBAL " if expression.args.get("global") else ""
    2361        return f"{global_}{kind}{this}{expressions}{collate}"
    2362
    2363    def set_sql(self, expression: exp.Set) -> str:
    2364        expressions = f" {self.expressions(expression, flat=True)}"
    2365        tag = " TAG" if expression.args.get("tag") else ""
    2366        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
    2367
    2368    def pragma_sql(self, expression: exp.Pragma) -> str:
    2369        return f"PRAGMA {self.sql(expression, 'this')}"
    2370
    2371    def lock_sql(self, expression: exp.Lock) -> str:
    2372        if not self.LOCKING_READS_SUPPORTED:
    2373            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2374            return ""
    2375
    2376        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2377        expressions = self.expressions(expression, flat=True)
    2378        expressions = f" OF {expressions}" if expressions else ""
    2379        wait = expression.args.get("wait")
    2380
    2381        if wait is not None:
    2382            if isinstance(wait, exp.Literal):
    2383                wait = f" WAIT {self.sql(wait)}"
    2384            else:
    2385                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2386
    2387        return f"{lock_type}{expressions}{wait or ''}"
    2388
    2389    def literal_sql(self, expression: exp.Literal) -> str:
    2390        text = expression.this or ""
    2391        if expression.is_string:
    2392            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2393        return text
    2394
    2395    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2396        if self.dialect.ESCAPED_SEQUENCES:
    2397            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2398            text = "".join(
    2399                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2400            )
    2401
    2402        return self._replace_line_breaks(text).replace(
    2403            self.dialect.QUOTE_END, self._escaped_quote_end
    2404        )
    2405
    2406    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2407        local = " LOCAL" if expression.args.get("local") else ""
    2408        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2409        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2410        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2411        partition = self.sql(expression, "partition")
    2412        partition = f" {partition}" if partition else ""
    2413        input_format = self.sql(expression, "input_format")
    2414        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2415        serde = self.sql(expression, "serde")
    2416        serde = f" SERDE {serde}" if serde else ""
    2417        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    2418
    2419    def null_sql(self, *_) -> str:
    2420        return "NULL"
    2421
    2422    def boolean_sql(self, expression: exp.Boolean) -> str:
    2423        return "TRUE" if expression.this else "FALSE"
    2424
    2425    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2426        this = self.sql(expression, "this")
    2427        this = f"{this} " if this else this
    2428        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2429        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2430
    2431    def withfill_sql(self, expression: exp.WithFill) -> str:
    2432        from_sql = self.sql(expression, "from")
    2433        from_sql = f" FROM {from_sql}" if from_sql else ""
    2434        to_sql = self.sql(expression, "to")
    2435        to_sql = f" TO {to_sql}" if to_sql else ""
    2436        step_sql = self.sql(expression, "step")
    2437        step_sql = f" STEP {step_sql}" if step_sql else ""
    2438        interpolated_values = [
    2439            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2440            if isinstance(e, exp.Alias)
    2441            else self.sql(e, "this")
    2442            for e in expression.args.get("interpolate") or []
    2443        ]
    2444        interpolate = (
    2445            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2446        )
    2447        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
    2448
    2449    def cluster_sql(self, expression: exp.Cluster) -> str:
    2450        return self.op_expressions("CLUSTER BY", expression)
    2451
    2452    def distribute_sql(self, expression: exp.Distribute) -> str:
    2453        return self.op_expressions("DISTRIBUTE BY", expression)
    2454
    2455    def sort_sql(self, expression: exp.Sort) -> str:
    2456        return self.op_expressions("SORT BY", expression)
    2457
    2458    def ordered_sql(self, expression: exp.Ordered) -> str:
    2459        desc = expression.args.get("desc")
    2460        asc = not desc
    2461
    2462        nulls_first = expression.args.get("nulls_first")
    2463        nulls_last = not nulls_first
    2464        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2465        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2466        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2467
    2468        this = self.sql(expression, "this")
    2469
    2470        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2471        nulls_sort_change = ""
    2472        if nulls_first and (
    2473            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2474        ):
    2475            nulls_sort_change = " NULLS FIRST"
    2476        elif (
    2477            nulls_last
    2478            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2479            and not nulls_are_last
    2480        ):
    2481            nulls_sort_change = " NULLS LAST"
    2482
    2483        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2484        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2485            window = expression.find_ancestor(exp.Window, exp.Select)
    2486            if isinstance(window, exp.Window) and window.args.get("spec"):
    2487                self.unsupported(
    2488                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2489                )
    2490                nulls_sort_change = ""
    2491            elif self.NULL_ORDERING_SUPPORTED is False and (
    2492                (asc and nulls_sort_change == " NULLS LAST")
    2493                or (desc and nulls_sort_change == " NULLS FIRST")
    2494            ):
    2495                # BigQuery does not allow these ordering/nulls combinations when used under
    2496                # an aggregation func or under a window containing one
    2497                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2498
    2499                if isinstance(ancestor, exp.Window):
    2500                    ancestor = ancestor.this
    2501                if isinstance(ancestor, exp.AggFunc):
    2502                    self.unsupported(
    2503                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2504                    )
    2505                    nulls_sort_change = ""
    2506            elif self.NULL_ORDERING_SUPPORTED is None:
    2507                if expression.this.is_int:
    2508                    self.unsupported(
    2509                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2510                    )
    2511                elif not isinstance(expression.this, exp.Rand):
    2512                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2513                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2514                nulls_sort_change = ""
    2515
    2516        with_fill = self.sql(expression, "with_fill")
    2517        with_fill = f" {with_fill}" if with_fill else ""
    2518
    2519        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
    2520
    2521    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2522        window_frame = self.sql(expression, "window_frame")
    2523        window_frame = f"{window_frame} " if window_frame else ""
    2524
    2525        this = self.sql(expression, "this")
    2526
    2527        return f"{window_frame}{this}"
    2528
    2529    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2530        partition = self.partition_by_sql(expression)
    2531        order = self.sql(expression, "order")
    2532        measures = self.expressions(expression, key="measures")
    2533        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2534        rows = self.sql(expression, "rows")
    2535        rows = self.seg(rows) if rows else ""
    2536        after = self.sql(expression, "after")
    2537        after = self.seg(after) if after else ""
    2538        pattern = self.sql(expression, "pattern")
    2539        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2540        definition_sqls = [
    2541            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2542            for definition in expression.args.get("define", [])
    2543        ]
    2544        definitions = self.expressions(sqls=definition_sqls)
    2545        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2546        body = "".join(
    2547            (
    2548                partition,
    2549                order,
    2550                measures,
    2551                rows,
    2552                after,
    2553                pattern,
    2554                define,
    2555            )
    2556        )
    2557        alias = self.sql(expression, "alias")
    2558        alias = f" {alias}" if alias else ""
    2559        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    2560
    2561    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2562        limit = expression.args.get("limit")
    2563
    2564        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2565            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2566        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2567            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2568
    2569        return csv(
    2570            *sqls,
    2571            *[self.sql(join) for join in expression.args.get("joins") or []],
    2572            self.sql(expression, "match"),
    2573            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2574            self.sql(expression, "prewhere"),
    2575            self.sql(expression, "where"),
    2576            self.sql(expression, "connect"),
    2577            self.sql(expression, "group"),
    2578            self.sql(expression, "having"),
    2579            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2580            self.sql(expression, "order"),
    2581            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2582            *self.after_limit_modifiers(expression),
    2583            self.options_modifier(expression),
    2584            sep="",
    2585        )
    2586
    2587    def options_modifier(self, expression: exp.Expression) -> str:
    2588        options = self.expressions(expression, key="options")
    2589        return f" {options}" if options else ""
    2590
    2591    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2592        self.unsupported("Unsupported query option.")
    2593        return ""
    2594
    2595    def offset_limit_modifiers(
    2596        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2597    ) -> t.List[str]:
    2598        return [
    2599            self.sql(expression, "offset") if fetch else self.sql(limit),
    2600            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2601        ]
    2602
    2603    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    2604        locks = self.expressions(expression, key="locks", sep=" ")
    2605        locks = f" {locks}" if locks else ""
    2606        return [locks, self.sql(expression, "sample")]
    2607
    2608    def select_sql(self, expression: exp.Select) -> str:
    2609        into = expression.args.get("into")
    2610        if not self.SUPPORTS_SELECT_INTO and into:
    2611            into.pop()
    2612
    2613        hint = self.sql(expression, "hint")
    2614        distinct = self.sql(expression, "distinct")
    2615        distinct = f" {distinct}" if distinct else ""
    2616        kind = self.sql(expression, "kind")
    2617
    2618        limit = expression.args.get("limit")
    2619        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2620            top = self.limit_sql(limit, top=True)
    2621            limit.pop()
    2622        else:
    2623            top = ""
    2624
    2625        expressions = self.expressions(expression)
    2626
    2627        if kind:
    2628            if kind in self.SELECT_KINDS:
    2629                kind = f" AS {kind}"
    2630            else:
    2631                if kind == "STRUCT":
    2632                    expressions = self.expressions(
    2633                        sqls=[
    2634                            self.sql(
    2635                                exp.Struct(
    2636                                    expressions=[
    2637                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2638                                        if isinstance(e, exp.Alias)
    2639                                        else e
    2640                                        for e in expression.expressions
    2641                                    ]
    2642                                )
    2643                            )
    2644                        ]
    2645                    )
    2646                kind = ""
    2647
    2648        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2649        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2650
    2651        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2652        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2653        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2654        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2655        sql = self.query_modifiers(
    2656            expression,
    2657            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2658            self.sql(expression, "into", comment=False),
    2659            self.sql(expression, "from", comment=False),
    2660        )
    2661
    2662        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2663        if expression.args.get("with"):
    2664            sql = self.maybe_comment(sql, expression)
    2665            expression.pop_comments()
    2666
    2667        sql = self.prepend_ctes(expression, sql)
    2668
    2669        if not self.SUPPORTS_SELECT_INTO and into:
    2670            if into.args.get("temporary"):
    2671                table_kind = " TEMPORARY"
    2672            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2673                table_kind = " UNLOGGED"
    2674            else:
    2675                table_kind = ""
    2676            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2677
    2678        return sql
    2679
    2680    def schema_sql(self, expression: exp.Schema) -> str:
    2681        this = self.sql(expression, "this")
    2682        sql = self.schema_columns_sql(expression)
    2683        return f"{this} {sql}" if this and sql else this or sql
    2684
    2685    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2686        if expression.expressions:
    2687            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2688        return ""
    2689
    2690    def star_sql(self, expression: exp.Star) -> str:
    2691        except_ = self.expressions(expression, key="except", flat=True)
    2692        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2693        replace = self.expressions(expression, key="replace", flat=True)
    2694        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2695        rename = self.expressions(expression, key="rename", flat=True)
    2696        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2697        return f"*{except_}{replace}{rename}"
    2698
    2699    def parameter_sql(self, expression: exp.Parameter) -> str:
    2700        this = self.sql(expression, "this")
    2701        return f"{self.PARAMETER_TOKEN}{this}"
    2702
    2703    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2704        this = self.sql(expression, "this")
    2705        kind = expression.text("kind")
    2706        if kind:
    2707            kind = f"{kind}."
    2708        return f"@@{kind}{this}"
    2709
    2710    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2711        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
    2712
    2713    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2714        alias = self.sql(expression, "alias")
    2715        alias = f"{sep}{alias}" if alias else ""
    2716        sample = self.sql(expression, "sample")
    2717        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2718            alias = f"{sample}{alias}"
    2719
    2720            # Set to None so it's not generated again by self.query_modifiers()
    2721            expression.set("sample", None)
    2722
    2723        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2724        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2725        return self.prepend_ctes(expression, sql)
    2726
    2727    def qualify_sql(self, expression: exp.Qualify) -> str:
    2728        this = self.indent(self.sql(expression, "this"))
    2729        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    2730
    2731    def unnest_sql(self, expression: exp.Unnest) -> str:
    2732        args = self.expressions(expression, flat=True)
    2733
    2734        alias = expression.args.get("alias")
    2735        offset = expression.args.get("offset")
    2736
    2737        if self.UNNEST_WITH_ORDINALITY:
    2738            if alias and isinstance(offset, exp.Expression):
    2739                alias.append("columns", offset)
    2740
    2741        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2742            columns = alias.columns
    2743            alias = self.sql(columns[0]) if columns else ""
    2744        else:
    2745            alias = self.sql(alias)
    2746
    2747        alias = f" AS {alias}" if alias else alias
    2748        if self.UNNEST_WITH_ORDINALITY:
    2749            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2750        else:
    2751            if isinstance(offset, exp.Expression):
    2752                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2753            elif offset:
    2754                suffix = f"{alias} WITH OFFSET"
    2755            else:
    2756                suffix = alias
    2757
    2758        return f"UNNEST({args}){suffix}"
    2759
    2760    def prewhere_sql(self, expression: exp.PreWhere) -> str:
    2761        return ""
    2762
    2763    def where_sql(self, expression: exp.Where) -> str:
    2764        this = self.indent(self.sql(expression, "this"))
    2765        return f"{self.seg('WHERE')}{self.sep()}{this}"
    2766
    2767    def window_sql(self, expression: exp.Window) -> str:
    2768        this = self.sql(expression, "this")
    2769        partition = self.partition_by_sql(expression)
    2770        order = expression.args.get("order")
    2771        order = self.order_sql(order, flat=True) if order else ""
    2772        spec = self.sql(expression, "spec")
    2773        alias = self.sql(expression, "alias")
    2774        over = self.sql(expression, "over") or "OVER"
    2775
    2776        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2777
    2778        first = expression.args.get("first")
    2779        if first is None:
    2780            first = ""
    2781        else:
    2782            first = "FIRST" if first else "LAST"
    2783
    2784        if not partition and not order and not spec and alias:
    2785            return f"{this} {alias}"
    2786
    2787        args = self.format_args(
    2788            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
    2789        )
    2790        return f"{this} ({args})"
    2791
    2792    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2793        partition = self.expressions(expression, key="partition_by", flat=True)
    2794        return f"PARTITION BY {partition}" if partition else ""
    2795
    2796    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2797        kind = self.sql(expression, "kind")
    2798        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2799        end = (
    2800            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2801            or "CURRENT ROW"
    2802        )
    2803        return f"{kind} BETWEEN {start} AND {end}"
    2804
    2805    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2806        this = self.sql(expression, "this")
    2807        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2808        return f"{this} WITHIN GROUP ({expression_sql})"
    2809
    2810    def between_sql(self, expression: exp.Between) -> str:
    2811        this = self.sql(expression, "this")
    2812        low = self.sql(expression, "low")
    2813        high = self.sql(expression, "high")
    2814        return f"{this} BETWEEN {low} AND {high}"
    2815
    2816    def bracket_offset_expressions(
    2817        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2818    ) -> t.List[exp.Expression]:
    2819        return apply_index_offset(
    2820            expression.this,
    2821            expression.expressions,
    2822            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2823            dialect=self.dialect,
    2824        )
    2825
    2826    def bracket_sql(self, expression: exp.Bracket) -> str:
    2827        expressions = self.bracket_offset_expressions(expression)
    2828        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2829        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    2830
    2831    def all_sql(self, expression: exp.All) -> str:
    2832        return f"ALL {self.wrap(expression)}"
    2833
    2834    def any_sql(self, expression: exp.Any) -> str:
    2835        this = self.sql(expression, "this")
    2836        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2837            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2838                this = self.wrap(this)
    2839            return f"ANY{this}"
    2840        return f"ANY {this}"
    2841
    2842    def exists_sql(self, expression: exp.Exists) -> str:
    2843        return f"EXISTS{self.wrap(expression)}"
    2844
    2845    def case_sql(self, expression: exp.Case) -> str:
    2846        this = self.sql(expression, "this")
    2847        statements = [f"CASE {this}" if this else "CASE"]
    2848
    2849        for e in expression.args["ifs"]:
    2850            statements.append(f"WHEN {self.sql(e, 'this')}")
    2851            statements.append(f"THEN {self.sql(e, 'true')}")
    2852
    2853        default = self.sql(expression, "default")
    2854
    2855        if default:
    2856            statements.append(f"ELSE {default}")
    2857
    2858        statements.append("END")
    2859
    2860        if self.pretty and self.too_wide(statements):
    2861            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2862
    2863        return " ".join(statements)
    2864
    2865    def constraint_sql(self, expression: exp.Constraint) -> str:
    2866        this = self.sql(expression, "this")
    2867        expressions = self.expressions(expression, flat=True)
    2868        return f"CONSTRAINT {this} {expressions}"
    2869
    2870    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2871        order = expression.args.get("order")
    2872        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2873        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    2874
    2875    def extract_sql(self, expression: exp.Extract) -> str:
    2876        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2877        expression_sql = self.sql(expression, "expression")
    2878        return f"EXTRACT({this} FROM {expression_sql})"
    2879
    2880    def trim_sql(self, expression: exp.Trim) -> str:
    2881        trim_type = self.sql(expression, "position")
    2882
    2883        if trim_type == "LEADING":
    2884            func_name = "LTRIM"
    2885        elif trim_type == "TRAILING":
    2886            func_name = "RTRIM"
    2887        else:
    2888            func_name = "TRIM"
    2889
    2890        return self.func(func_name, expression.this, expression.expression)
    2891
    2892    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2893        args = expression.expressions
    2894        if isinstance(expression, exp.ConcatWs):
    2895            args = args[1:]  # Skip the delimiter
    2896
    2897        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2898            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2899
    2900        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2901            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2902
    2903        return args
    2904
    2905    def concat_sql(self, expression: exp.Concat) -> str:
    2906        expressions = self.convert_concat_args(expression)
    2907
    2908        # Some dialects don't allow a single-argument CONCAT call
    2909        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2910            return self.sql(expressions[0])
    2911
    2912        return self.func("CONCAT", *expressions)
    2913
    2914    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2915        return self.func(
    2916            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2917        )
    2918
    2919    def check_sql(self, expression: exp.Check) -> str:
    2920        this = self.sql(expression, key="this")
    2921        return f"CHECK ({this})"
    2922
    2923    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2924        expressions = self.expressions(expression, flat=True)
    2925        expressions = f" ({expressions})" if expressions else ""
    2926        reference = self.sql(expression, "reference")
    2927        reference = f" {reference}" if reference else ""
    2928        delete = self.sql(expression, "delete")
    2929        delete = f" ON DELETE {delete}" if delete else ""
    2930        update = self.sql(expression, "update")
    2931        update = f" ON UPDATE {update}" if update else ""
    2932        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2933        options = f" {options}" if options else ""
    2934        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
    2935
    2936    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2937        expressions = self.expressions(expression, flat=True)
    2938        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2939        options = f" {options}" if options else ""
    2940        return f"PRIMARY KEY ({expressions}){options}"
    2941
    2942    def if_sql(self, expression: exp.If) -> str:
    2943        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
    2944
    2945    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2946        modifier = expression.args.get("modifier")
    2947        modifier = f" {modifier}" if modifier else ""
    2948        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    2949
    2950    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2951        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
    2952
    2953    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2954        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2955
    2956        if expression.args.get("escape"):
    2957            path = self.escape_str(path)
    2958
    2959        if self.QUOTE_JSON_PATH:
    2960            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2961
    2962        return path
    2963
    2964    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2965        if isinstance(expression, exp.JSONPathPart):
    2966            transform = self.TRANSFORMS.get(expression.__class__)
    2967            if not callable(transform):
    2968                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2969                return ""
    2970
    2971            return transform(self, expression)
    2972
    2973        if isinstance(expression, int):
    2974            return str(expression)
    2975
    2976        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2977            escaped = expression.replace("'", "\\'")
    2978            escaped = f"\\'{expression}\\'"
    2979        else:
    2980            escaped = expression.replace('"', '\\"')
    2981            escaped = f'"{escaped}"'
    2982
    2983        return escaped
    2984
    2985    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2986        return f"{self.sql(expression, 'this')} FORMAT JSON"
    2987
    2988    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2989        null_handling = expression.args.get("null_handling")
    2990        null_handling = f" {null_handling}" if null_handling else ""
    2991
    2992        unique_keys = expression.args.get("unique_keys")
    2993        if unique_keys is not None:
    2994            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2995        else:
    2996            unique_keys = ""
    2997
    2998        return_type = self.sql(expression, "return_type")
    2999        return_type = f" RETURNING {return_type}" if return_type else ""
    3000        encoding = self.sql(expression, "encoding")
    3001        encoding = f" ENCODING {encoding}" if encoding else ""
    3002
    3003        return self.func(
    3004            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    3005            *expression.expressions,
    3006            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    3007        )
    3008
    3009    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
    3010        return self.jsonobject_sql(expression)
    3011
    3012    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    3013        null_handling = expression.args.get("null_handling")
    3014        null_handling = f" {null_handling}" if null_handling else ""
    3015        return_type = self.sql(expression, "return_type")
    3016        return_type = f" RETURNING {return_type}" if return_type else ""
    3017        strict = " STRICT" if expression.args.get("strict") else ""
    3018        return self.func(
    3019            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    3020        )
    3021
    3022    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    3023        this = self.sql(expression, "this")
    3024        order = self.sql(expression, "order")
    3025        null_handling = expression.args.get("null_handling")
    3026        null_handling = f" {null_handling}" if null_handling else ""
    3027        return_type = self.sql(expression, "return_type")
    3028        return_type = f" RETURNING {return_type}" if return_type else ""
    3029        strict = " STRICT" if expression.args.get("strict") else ""
    3030        return self.func(
    3031            "JSON_ARRAYAGG",
    3032            this,
    3033            suffix=f"{order}{null_handling}{return_type}{strict})",
    3034        )
    3035
    3036    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    3037        path = self.sql(expression, "path")
    3038        path = f" PATH {path}" if path else ""
    3039        nested_schema = self.sql(expression, "nested_schema")
    3040
    3041        if nested_schema:
    3042            return f"NESTED{path} {nested_schema}"
    3043
    3044        this = self.sql(expression, "this")
    3045        kind = self.sql(expression, "kind")
    3046        kind = f" {kind}" if kind else ""
    3047        return f"{this}{kind}{path}"
    3048
    3049    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    3050        return self.func("COLUMNS", *expression.expressions)
    3051
    3052    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    3053        this = self.sql(expression, "this")
    3054        path = self.sql(expression, "path")
    3055        path = f", {path}" if path else ""
    3056        error_handling = expression.args.get("error_handling")
    3057        error_handling = f" {error_handling}" if error_handling else ""
    3058        empty_handling = expression.args.get("empty_handling")
    3059        empty_handling = f" {empty_handling}" if empty_handling else ""
    3060        schema = self.sql(expression, "schema")
    3061        return self.func(
    3062            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    3063        )
    3064
    3065    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    3066        this = self.sql(expression, "this")
    3067        kind = self.sql(expression, "kind")
    3068        path = self.sql(expression, "path")
    3069        path = f" {path}" if path else ""
    3070        as_json = " AS JSON" if expression.args.get("as_json") else ""
    3071        return f"{this} {kind}{path}{as_json}"
    3072
    3073    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    3074        this = self.sql(expression, "this")
    3075        path = self.sql(expression, "path")
    3076        path = f", {path}" if path else ""
    3077        expressions = self.expressions(expression)
    3078        with_ = (
    3079            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    3080            if expressions
    3081            else ""
    3082        )
    3083        return f"OPENJSON({this}{path}){with_}"
    3084
    3085    def in_sql(self, expression: exp.In) -> str:
    3086        query = expression.args.get("query")
    3087        unnest = expression.args.get("unnest")
    3088        field = expression.args.get("field")
    3089        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3090
    3091        if query:
    3092            in_sql = self.sql(query)
    3093        elif unnest:
    3094            in_sql = self.in_unnest_op(unnest)
    3095        elif field:
    3096            in_sql = self.sql(field)
    3097        else:
    3098            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    3099
    3100        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    3101
    3102    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    3103        return f"(SELECT {self.sql(unnest)})"
    3104
    3105    def interval_sql(self, expression: exp.Interval) -> str:
    3106        unit = self.sql(expression, "unit")
    3107        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3108            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3109        unit = f" {unit}" if unit else ""
    3110
    3111        if self.SINGLE_STRING_INTERVAL:
    3112            this = expression.this.name if expression.this else ""
    3113            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    3114
    3115        this = self.sql(expression, "this")
    3116        if this:
    3117            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3118            this = f" {this}" if unwrapped else f" ({this})"
    3119
    3120        return f"INTERVAL{this}{unit}"
    3121
    3122    def return_sql(self, expression: exp.Return) -> str:
    3123        return f"RETURN {self.sql(expression, 'this')}"
    3124
    3125    def reference_sql(self, expression: exp.Reference) -> str:
    3126        this = self.sql(expression, "this")
    3127        expressions = self.expressions(expression, flat=True)
    3128        expressions = f"({expressions})" if expressions else ""
    3129        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3130        options = f" {options}" if options else ""
    3131        return f"REFERENCES {this}{expressions}{options}"
    3132
    3133    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3134        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3135        parent = expression.parent
    3136        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3137        return self.func(
    3138            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3139        )
    3140
    3141    def paren_sql(self, expression: exp.Paren) -> str:
    3142        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    3143        return f"({sql}{self.seg(')', sep='')}"
    3144
    3145    def neg_sql(self, expression: exp.Neg) -> str:
    3146        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    3147        this_sql = self.sql(expression, "this")
    3148        sep = " " if this_sql[0] == "-" else ""
    3149        return f"-{sep}{this_sql}"
    3150
    3151    def not_sql(self, expression: exp.Not) -> str:
    3152        return f"NOT {self.sql(expression, 'this')}"
    3153
    3154    def alias_sql(self, expression: exp.Alias) -> str:
    3155        alias = self.sql(expression, "alias")
    3156        alias = f" AS {alias}" if alias else ""
    3157        return f"{self.sql(expression, 'this')}{alias}"
    3158
    3159    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3160        alias = expression.args["alias"]
    3161
    3162        parent = expression.parent
    3163        pivot = parent and parent.parent
    3164
    3165        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
    3166            identifier_alias = isinstance(alias, exp.Identifier)
    3167            literal_alias = isinstance(alias, exp.Literal)
    3168
    3169            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3170                alias.replace(exp.Literal.string(alias.output_name))
    3171            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3172                alias.replace(exp.to_identifier(alias.output_name))
    3173
    3174        return self.alias_sql(expression)
    3175
    3176    def aliases_sql(self, expression: exp.Aliases) -> str:
    3177        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    3178
    3179    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3180        this = self.sql(expression, "this")
    3181        index = self.sql(expression, "expression")
    3182        return f"{this} AT {index}"
    3183
    3184    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3185        this = self.sql(expression, "this")
    3186        zone = self.sql(expression, "zone")
    3187        return f"{this} AT TIME ZONE {zone}"
    3188
    3189    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3190        this = self.sql(expression, "this")
    3191        zone = self.sql(expression, "zone")
    3192        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
    3193
    3194    def add_sql(self, expression: exp.Add) -> str:
    3195        return self.binary(expression, "+")
    3196
    3197    def and_sql(
    3198        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3199    ) -> str:
    3200        return self.connector_sql(expression, "AND", stack)
    3201
    3202    def or_sql(
    3203        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3204    ) -> str:
    3205        return self.connector_sql(expression, "OR", stack)
    3206
    3207    def xor_sql(
    3208        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3209    ) -> str:
    3210        return self.connector_sql(expression, "XOR", stack)
    3211
    3212    def connector_sql(
    3213        self,
    3214        expression: exp.Connector,
    3215        op: str,
    3216        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3217    ) -> str:
    3218        if stack is not None:
    3219            if expression.expressions:
    3220                stack.append(self.expressions(expression, sep=f" {op} "))
    3221            else:
    3222                stack.append(expression.right)
    3223                if expression.comments and self.comments:
    3224                    for comment in expression.comments:
    3225                        if comment:
    3226                            op += f" /*{self.pad_comment(comment)}*/"
    3227                stack.extend((op, expression.left))
    3228            return op
    3229
    3230        stack = [expression]
    3231        sqls: t.List[str] = []
    3232        ops = set()
    3233
    3234        while stack:
    3235            node = stack.pop()
    3236            if isinstance(node, exp.Connector):
    3237                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3238            else:
    3239                sql = self.sql(node)
    3240                if sqls and sqls[-1] in ops:
    3241                    sqls[-1] += f" {sql}"
    3242                else:
    3243                    sqls.append(sql)
    3244
    3245        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3246        return sep.join(sqls)
    3247
    3248    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    3249        return self.binary(expression, "&")
    3250
    3251    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    3252        return self.binary(expression, "<<")
    3253
    3254    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    3255        return f"~{self.sql(expression, 'this')}"
    3256
    3257    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    3258        return self.binary(expression, "|")
    3259
    3260    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    3261        return self.binary(expression, ">>")
    3262
    3263    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    3264        return self.binary(expression, "^")
    3265
    3266    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3267        format_sql = self.sql(expression, "format")
    3268        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3269        to_sql = self.sql(expression, "to")
    3270        to_sql = f" {to_sql}" if to_sql else ""
    3271        action = self.sql(expression, "action")
    3272        action = f" {action}" if action else ""
    3273        default = self.sql(expression, "default")
    3274        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3275        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
    3276
    3277    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    3278        zone = self.sql(expression, "this")
    3279        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    3280
    3281    def collate_sql(self, expression: exp.Collate) -> str:
    3282        if self.COLLATE_IS_FUNC:
    3283            return self.function_fallback_sql(expression)
    3284        return self.binary(expression, "COLLATE")
    3285
    3286    def command_sql(self, expression: exp.Command) -> str:
    3287        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
    3288
    3289    def comment_sql(self, expression: exp.Comment) -> str:
    3290        this = self.sql(expression, "this")
    3291        kind = expression.args["kind"]
    3292        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3293        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3294        expression_sql = self.sql(expression, "expression")
    3295        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
    3296
    3297    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3298        this = self.sql(expression, "this")
    3299        delete = " DELETE" if expression.args.get("delete") else ""
    3300        recompress = self.sql(expression, "recompress")
    3301        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3302        to_disk = self.sql(expression, "to_disk")
    3303        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3304        to_volume = self.sql(expression, "to_volume")
    3305        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3306        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    3307
    3308    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3309        where = self.sql(expression, "where")
    3310        group = self.sql(expression, "group")
    3311        aggregates = self.expressions(expression, key="aggregates")
    3312        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3313
    3314        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3315            return f"TTL {self.expressions(expression, flat=True)}"
    3316
    3317        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    3318
    3319    def transaction_sql(self, expression: exp.Transaction) -> str:
    3320        return "BEGIN"
    3321
    3322    def commit_sql(self, expression: exp.Commit) -> str:
    3323        chain = expression.args.get("chain")
    3324        if chain is not None:
    3325            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3326
    3327        return f"COMMIT{chain or ''}"
    3328
    3329    def rollback_sql(self, expression: exp.Rollback) -> str:
    3330        savepoint = expression.args.get("savepoint")
    3331        savepoint = f" TO {savepoint}" if savepoint else ""
    3332        return f"ROLLBACK{savepoint}"
    3333
    3334    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3335        this = self.sql(expression, "this")
    3336
    3337        dtype = self.sql(expression, "dtype")
    3338        if dtype:
    3339            collate = self.sql(expression, "collate")
    3340            collate = f" COLLATE {collate}" if collate else ""
    3341            using = self.sql(expression, "using")
    3342            using = f" USING {using}" if using else ""
    3343            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
    3344
    3345        default = self.sql(expression, "default")
    3346        if default:
    3347            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3348
    3349        comment = self.sql(expression, "comment")
    3350        if comment:
    3351            return f"ALTER COLUMN {this} COMMENT {comment}"
    3352
    3353        visible = expression.args.get("visible")
    3354        if visible:
    3355            return f"ALTER COLUMN {this} SET {visible}"
    3356
    3357        allow_null = expression.args.get("allow_null")
    3358        drop = expression.args.get("drop")
    3359
    3360        if not drop and not allow_null:
    3361            self.unsupported("Unsupported ALTER COLUMN syntax")
    3362
    3363        if allow_null is not None:
    3364            keyword = "DROP" if drop else "SET"
    3365            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3366
    3367        return f"ALTER COLUMN {this} DROP DEFAULT"
    3368
    3369    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
    3370        this = self.sql(expression, "this")
    3371
    3372        visible = expression.args.get("visible")
    3373        visible_sql = "VISIBLE" if visible else "INVISIBLE"
    3374
    3375        return f"ALTER INDEX {this} {visible_sql}"
    3376
    3377    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3378        this = self.sql(expression, "this")
    3379        if not isinstance(expression.this, exp.Var):
    3380            this = f"KEY DISTKEY {this}"
    3381        return f"ALTER DISTSTYLE {this}"
    3382
    3383    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3384        compound = " COMPOUND" if expression.args.get("compound") else ""
    3385        this = self.sql(expression, "this")
    3386        expressions = self.expressions(expression, flat=True)
    3387        expressions = f"({expressions})" if expressions else ""
    3388        return f"ALTER{compound} SORTKEY {this or expressions}"
    3389
    3390    def alterrename_sql(self, expression: exp.AlterRename) -> str:
    3391        if not self.RENAME_TABLE_WITH_DB:
    3392            # Remove db from tables
    3393            expression = expression.transform(
    3394                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3395            ).assert_is(exp.AlterRename)
    3396        this = self.sql(expression, "this")
    3397        return f"RENAME TO {this}"
    3398
    3399    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3400        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3401        old_column = self.sql(expression, "this")
    3402        new_column = self.sql(expression, "to")
    3403        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
    3404
    3405    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3406        exprs = self.expressions(expression, flat=True)
    3407        return f"SET {exprs}"
    3408
    3409    def alter_sql(self, expression: exp.Alter) -> str:
    3410        actions = expression.args["actions"]
    3411
    3412        if isinstance(actions[0], exp.ColumnDef):
    3413            actions = self.add_column_sql(expression)
    3414        elif isinstance(actions[0], exp.Schema):
    3415            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3416        elif isinstance(actions[0], exp.Delete):
    3417            actions = self.expressions(expression, key="actions", flat=True)
    3418        elif isinstance(actions[0], exp.Query):
    3419            actions = "AS " + self.expressions(expression, key="actions")
    3420        else:
    3421            actions = self.expressions(expression, key="actions", flat=True)
    3422
    3423        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3424        on_cluster = self.sql(expression, "cluster")
    3425        on_cluster = f" {on_cluster}" if on_cluster else ""
    3426        only = " ONLY" if expression.args.get("only") else ""
    3427        options = self.expressions(expression, key="options")
    3428        options = f", {options}" if options else ""
    3429        kind = self.sql(expression, "kind")
    3430        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3431
    3432        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
    3433
    3434    def add_column_sql(self, expression: exp.Alter) -> str:
    3435        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3436            return self.expressions(
    3437                expression,
    3438                key="actions",
    3439                prefix="ADD COLUMN ",
    3440                skip_first=True,
    3441            )
    3442        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
    3443
    3444    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3445        expressions = self.expressions(expression)
    3446        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3447        return f"DROP{exists}{expressions}"
    3448
    3449    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3450        return f"ADD {self.expressions(expression)}"
    3451
    3452    def distinct_sql(self, expression: exp.Distinct) -> str:
    3453        this = self.expressions(expression, flat=True)
    3454
    3455        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3456            case = exp.case()
    3457            for arg in expression.expressions:
    3458                case = case.when(arg.is_(exp.null()), exp.null())
    3459            this = self.sql(case.else_(f"({this})"))
    3460
    3461        this = f" {this}" if this else ""
    3462
    3463        on = self.sql(expression, "on")
    3464        on = f" ON {on}" if on else ""
    3465        return f"DISTINCT{this}{on}"
    3466
    3467    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    3468        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
    3469
    3470    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    3471        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
    3472
    3473    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3474        this_sql = self.sql(expression, "this")
    3475        expression_sql = self.sql(expression, "expression")
    3476        kind = "MAX" if expression.args.get("max") else "MIN"
    3477        return f"{this_sql} HAVING {kind} {expression_sql}"
    3478
    3479    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3480        return self.sql(
    3481            exp.Cast(
    3482                this=exp.Div(this=expression.this, expression=expression.expression),
    3483                to=exp.DataType(this=exp.DataType.Type.INT),
    3484            )
    3485        )
    3486
    3487    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3488        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3489            return self.func(
    3490                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3491            )
    3492        return self.binary(expression, "||")
    3493
    3494    def div_sql(self, expression: exp.Div) -> str:
    3495        l, r = expression.left, expression.right
    3496
    3497        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3498            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3499
    3500        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3501            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3502                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3503
    3504        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3505            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3506                return self.sql(
    3507                    exp.cast(
    3508                        l / r,
    3509                        to=exp.DataType.Type.BIGINT,
    3510                    )
    3511                )
    3512
    3513        return self.binary(expression, "/")
    3514
    3515    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3516        n = exp._wrap(expression.this, exp.Binary)
    3517        d = exp._wrap(expression.expression, exp.Binary)
    3518        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
    3519
    3520    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    3521        return self.binary(expression, "OVERLAPS")
    3522
    3523    def distance_sql(self, expression: exp.Distance) -> str:
    3524        return self.binary(expression, "<->")
    3525
    3526    def dot_sql(self, expression: exp.Dot) -> str:
    3527        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    3528
    3529    def eq_sql(self, expression: exp.EQ) -> str:
    3530        return self.binary(expression, "=")
    3531
    3532    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    3533        return self.binary(expression, ":=")
    3534
    3535    def escape_sql(self, expression: exp.Escape) -> str:
    3536        return self.binary(expression, "ESCAPE")
    3537
    3538    def glob_sql(self, expression: exp.Glob) -> str:
    3539        return self.binary(expression, "GLOB")
    3540
    3541    def gt_sql(self, expression: exp.GT) -> str:
    3542        return self.binary(expression, ">")
    3543
    3544    def gte_sql(self, expression: exp.GTE) -> str:
    3545        return self.binary(expression, ">=")
    3546
    3547    def ilike_sql(self, expression: exp.ILike) -> str:
    3548        return self.binary(expression, "ILIKE")
    3549
    3550    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    3551        return self.binary(expression, "ILIKE ANY")
    3552
    3553    def is_sql(self, expression: exp.Is) -> str:
    3554        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3555            return self.sql(
    3556                expression.this if expression.expression.this else exp.not_(expression.this)
    3557            )
    3558        return self.binary(expression, "IS")
    3559
    3560    def like_sql(self, expression: exp.Like) -> str:
    3561        return self.binary(expression, "LIKE")
    3562
    3563    def likeany_sql(self, expression: exp.LikeAny) -> str:
    3564        return self.binary(expression, "LIKE ANY")
    3565
    3566    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    3567        return self.binary(expression, "SIMILAR TO")
    3568
    3569    def lt_sql(self, expression: exp.LT) -> str:
    3570        return self.binary(expression, "<")
    3571
    3572    def lte_sql(self, expression: exp.LTE) -> str:
    3573        return self.binary(expression, "<=")
    3574
    3575    def mod_sql(self, expression: exp.Mod) -> str:
    3576        return self.binary(expression, "%")
    3577
    3578    def mul_sql(self, expression: exp.Mul) -> str:
    3579        return self.binary(expression, "*")
    3580
    3581    def neq_sql(self, expression: exp.NEQ) -> str:
    3582        return self.binary(expression, "<>")
    3583
    3584    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    3585        return self.binary(expression, "IS NOT DISTINCT FROM")
    3586
    3587    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    3588        return self.binary(expression, "IS DISTINCT FROM")
    3589
    3590    def slice_sql(self, expression: exp.Slice) -> str:
    3591        return self.binary(expression, ":")
    3592
    3593    def sub_sql(self, expression: exp.Sub) -> str:
    3594        return self.binary(expression, "-")
    3595
    3596    def trycast_sql(self, expression: exp.TryCast) -> str:
    3597        return self.cast_sql(expression, safe_prefix="TRY_")
    3598
    3599    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
    3600        return self.cast_sql(expression)
    3601
    3602    def try_sql(self, expression: exp.Try) -> str:
    3603        if not self.TRY_SUPPORTED:
    3604            self.unsupported("Unsupported TRY function")
    3605            return self.sql(expression, "this")
    3606
    3607        return self.func("TRY", expression.this)
    3608
    3609    def log_sql(self, expression: exp.Log) -> str:
    3610        this = expression.this
    3611        expr = expression.expression
    3612
    3613        if self.dialect.LOG_BASE_FIRST is False:
    3614            this, expr = expr, this
    3615        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3616            if this.name in ("2", "10"):
    3617                return self.func(f"LOG{this.name}", expr)
    3618
    3619            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3620
    3621        return self.func("LOG", this, expr)
    3622
    3623    def use_sql(self, expression: exp.Use) -> str:
    3624        kind = self.sql(expression, "kind")
    3625        kind = f" {kind}" if kind else ""
    3626        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
    3627        this = f" {this}" if this else ""
    3628        return f"USE{kind}{this}"
    3629
    3630    def binary(self, expression: exp.Binary, op: str) -> str:
    3631        sqls: t.List[str] = []
    3632        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3633        binary_type = type(expression)
    3634
    3635        while stack:
    3636            node = stack.pop()
    3637
    3638            if type(node) is binary_type:
    3639                op_func = node.args.get("operator")
    3640                if op_func:
    3641                    op = f"OPERATOR({self.sql(op_func)})"
    3642
    3643                stack.append(node.right)
    3644                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3645                stack.append(node.left)
    3646            else:
    3647                sqls.append(self.sql(node))
    3648
    3649        return "".join(sqls)
    3650
    3651    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3652        to_clause = self.sql(expression, "to")
    3653        if to_clause:
    3654            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3655
    3656        return self.function_fallback_sql(expression)
    3657
    3658    def function_fallback_sql(self, expression: exp.Func) -> str:
    3659        args = []
    3660
    3661        for key in expression.arg_types:
    3662            arg_value = expression.args.get(key)
    3663
    3664            if isinstance(arg_value, list):
    3665                for value in arg_value:
    3666                    args.append(value)
    3667            elif arg_value is not None:
    3668                args.append(arg_value)
    3669
    3670        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3671            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3672        else:
    3673            name = expression.sql_name()
    3674
    3675        return self.func(name, *args)
    3676
    3677    def func(
    3678        self,
    3679        name: str,
    3680        *args: t.Optional[exp.Expression | str],
    3681        prefix: str = "(",
    3682        suffix: str = ")",
    3683        normalize: bool = True,
    3684    ) -> str:
    3685        name = self.normalize_func(name) if normalize else name
    3686        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
    3687
    3688    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3689        arg_sqls = tuple(
    3690            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3691        )
    3692        if self.pretty and self.too_wide(arg_sqls):
    3693            return self.indent(
    3694                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3695            )
    3696        return sep.join(arg_sqls)
    3697
    3698    def too_wide(self, args: t.Iterable) -> bool:
    3699        return sum(len(arg) for arg in args) > self.max_text_width
    3700
    3701    def format_time(
    3702        self,
    3703        expression: exp.Expression,
    3704        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3705        inverse_time_trie: t.Optional[t.Dict] = None,
    3706    ) -> t.Optional[str]:
    3707        return format_time(
    3708            self.sql(expression, "format"),
    3709            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3710            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3711        )
    3712
    3713    def expressions(
    3714        self,
    3715        expression: t.Optional[exp.Expression] = None,
    3716        key: t.Optional[str] = None,
    3717        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3718        flat: bool = False,
    3719        indent: bool = True,
    3720        skip_first: bool = False,
    3721        skip_last: bool = False,
    3722        sep: str = ", ",
    3723        prefix: str = "",
    3724        dynamic: bool = False,
    3725        new_line: bool = False,
    3726    ) -> str:
    3727        expressions = expression.args.get(key or "expressions") if expression else sqls
    3728
    3729        if not expressions:
    3730            return ""
    3731
    3732        if flat:
    3733            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3734
    3735        num_sqls = len(expressions)
    3736        result_sqls = []
    3737
    3738        for i, e in enumerate(expressions):
    3739            sql = self.sql(e, comment=False)
    3740            if not sql:
    3741                continue
    3742
    3743            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3744
    3745            if self.pretty:
    3746                if self.leading_comma:
    3747                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3748                else:
    3749                    result_sqls.append(
    3750                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3751                    )
    3752            else:
    3753                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3754
    3755        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3756            if new_line:
    3757                result_sqls.insert(0, "")
    3758                result_sqls.append("")
    3759            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3760        else:
    3761            result_sql = "".join(result_sqls)
    3762
    3763        return (
    3764            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3765            if indent
    3766            else result_sql
    3767        )
    3768
    3769    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3770        flat = flat or isinstance(expression.parent, exp.Properties)
    3771        expressions_sql = self.expressions(expression, flat=flat)
    3772        if flat:
    3773            return f"{op} {expressions_sql}"
    3774        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    3775
    3776    def naked_property(self, expression: exp.Property) -> str:
    3777        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3778        if not property_name:
    3779            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3780        return f"{property_name} {self.sql(expression, 'this')}"
    3781
    3782    def tag_sql(self, expression: exp.Tag) -> str:
    3783        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    3784
    3785    def token_sql(self, token_type: TokenType) -> str:
    3786        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    3787
    3788    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3789        this = self.sql(expression, "this")
    3790        expressions = self.no_identify(self.expressions, expression)
    3791        expressions = (
    3792            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3793        )
    3794        return f"{this}{expressions}" if expressions.strip() != "" else this
    3795
    3796    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3797        this = self.sql(expression, "this")
    3798        expressions = self.expressions(expression, flat=True)
    3799        return f"{this}({expressions})"
    3800
    3801    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    3802        return self.binary(expression, "=>")
    3803
    3804    def when_sql(self, expression: exp.When) -> str:
    3805        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3806        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3807        condition = self.sql(expression, "condition")
    3808        condition = f" AND {condition}" if condition else ""
    3809
    3810        then_expression = expression.args.get("then")
    3811        if isinstance(then_expression, exp.Insert):
    3812            this = self.sql(then_expression, "this")
    3813            this = f"INSERT {this}" if this else "INSERT"
    3814            then = self.sql(then_expression, "expression")
    3815            then = f"{this} VALUES {then}" if then else this
    3816        elif isinstance(then_expression, exp.Update):
    3817            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3818                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3819            else:
    3820                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3821        else:
    3822            then = self.sql(then_expression)
    3823        return f"WHEN {matched}{source}{condition} THEN {then}"
    3824
    3825    def whens_sql(self, expression: exp.Whens) -> str:
    3826        return self.expressions(expression, sep=" ", indent=False)
    3827
    3828    def merge_sql(self, expression: exp.Merge) -> str:
    3829        table = expression.this
    3830        table_alias = ""
    3831
    3832        hints = table.args.get("hints")
    3833        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3834            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3835            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3836
    3837        this = self.sql(table)
    3838        using = f"USING {self.sql(expression, 'using')}"
    3839        on = f"ON {self.sql(expression, 'on')}"
    3840        whens = self.sql(expression, "whens")
    3841
    3842        returning = self.sql(expression, "returning")
    3843        if returning:
    3844            whens = f"{whens}{returning}"
    3845
    3846        sep = self.sep()
    3847
    3848        return self.prepend_ctes(
    3849            expression,
    3850            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    3851        )
    3852
    3853    @unsupported_args("format")
    3854    def tochar_sql(self, expression: exp.ToChar) -> str:
    3855        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
    3856
    3857    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3858        if not self.SUPPORTS_TO_NUMBER:
    3859            self.unsupported("Unsupported TO_NUMBER function")
    3860            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3861
    3862        fmt = expression.args.get("format")
    3863        if not fmt:
    3864            self.unsupported("Conversion format is required for TO_NUMBER")
    3865            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3866
    3867        return self.func("TO_NUMBER", expression.this, fmt)
    3868
    3869    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3870        this = self.sql(expression, "this")
    3871        kind = self.sql(expression, "kind")
    3872        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3873        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3874        return f"{this}({kind}{args})"
    3875
    3876    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3877        this = self.sql(expression, "this")
    3878        max = self.sql(expression, "max")
    3879        min = self.sql(expression, "min")
    3880        return f"{this}(MIN {min} MAX {max})"
    3881
    3882    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    3883        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    3884
    3885    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
    3886        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
    3887
    3888    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
    3889    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
    3890        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
    3891
    3892    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
    3893    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    3894        expressions = self.expressions(expression, flat=True)
    3895        expressions = f" {self.wrap(expressions)}" if expressions else ""
    3896        buckets = self.sql(expression, "buckets")
    3897        kind = self.sql(expression, "kind")
    3898        buckets = f" BUCKETS {buckets}" if buckets else ""
    3899        order = self.sql(expression, "order")
    3900        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
    3901
    3902    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    3903        return ""
    3904
    3905    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3906        expressions = self.expressions(expression, key="expressions", flat=True)
    3907        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3908        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3909        buckets = self.sql(expression, "buckets")
    3910        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
    3911
    3912    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3913        this = self.sql(expression, "this")
    3914        having = self.sql(expression, "having")
    3915
    3916        if having:
    3917            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3918
    3919        return self.func("ANY_VALUE", this)
    3920
    3921    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3922        transform = self.func("TRANSFORM", *expression.expressions)
    3923        row_format_before = self.sql(expression, "row_format_before")
    3924        row_format_before = f" {row_format_before}" if row_format_before else ""
    3925        record_writer = self.sql(expression, "record_writer")
    3926        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3927        using = f" USING {self.sql(expression, 'command_script')}"
    3928        schema = self.sql(expression, "schema")
    3929        schema = f" AS {schema}" if schema else ""
    3930        row_format_after = self.sql(expression, "row_format_after")
    3931        row_format_after = f" {row_format_after}" if row_format_after else ""
    3932        record_reader = self.sql(expression, "record_reader")
    3933        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3934        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
    3935
    3936    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3937        key_block_size = self.sql(expression, "key_block_size")
    3938        if key_block_size:
    3939            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3940
    3941        using = self.sql(expression, "using")
    3942        if using:
    3943            return f"USING {using}"
    3944
    3945        parser = self.sql(expression, "parser")
    3946        if parser:
    3947            return f"WITH PARSER {parser}"
    3948
    3949        comment = self.sql(expression, "comment")
    3950        if comment:
    3951            return f"COMMENT {comment}"
    3952
    3953        visible = expression.args.get("visible")
    3954        if visible is not None:
    3955            return "VISIBLE" if visible else "INVISIBLE"
    3956
    3957        engine_attr = self.sql(expression, "engine_attr")
    3958        if engine_attr:
    3959            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3960
    3961        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3962        if secondary_engine_attr:
    3963            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3964
    3965        self.unsupported("Unsupported index constraint option.")
    3966        return ""
    3967
    3968    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
    3969        enforced = " ENFORCED" if expression.args.get("enforced") else ""
    3970        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
    3971
    3972    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3973        kind = self.sql(expression, "kind")
    3974        kind = f"{kind} INDEX" if kind else "INDEX"
    3975        this = self.sql(expression, "this")
    3976        this = f" {this}" if this else ""
    3977        index_type = self.sql(expression, "index_type")
    3978        index_type = f" USING {index_type}" if index_type else ""
    3979        expressions = self.expressions(expression, flat=True)
    3980        expressions = f" ({expressions})" if expressions else ""
    3981        options = self.expressions(expression, key="options", sep=" ")
    3982        options = f" {options}" if options else ""
    3983        return f"{kind}{this}{index_type}{expressions}{options}"
    3984
    3985    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3986        if self.NVL2_SUPPORTED:
    3987            return self.function_fallback_sql(expression)
    3988
    3989        case = exp.Case().when(
    3990            expression.this.is_(exp.null()).not_(copy=False),
    3991            expression.args["true"],
    3992            copy=False,
    3993        )
    3994        else_cond = expression.args.get("false")
    3995        if else_cond:
    3996            case.else_(else_cond, copy=False)
    3997
    3998        return self.sql(case)
    3999
    4000    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    4001        this = self.sql(expression, "this")
    4002        expr = self.sql(expression, "expression")
    4003        iterator = self.sql(expression, "iterator")
    4004        condition = self.sql(expression, "condition")
    4005        condition = f" IF {condition}" if condition else ""
    4006        return f"{this} FOR {expr} IN {iterator}{condition}"
    4007
    4008    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    4009        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
    4010
    4011    def opclass_sql(self, expression: exp.Opclass) -> str:
    4012        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    4013
    4014    def predict_sql(self, expression: exp.Predict) -> str:
    4015        model = self.sql(expression, "this")
    4016        model = f"MODEL {model}"
    4017        table = self.sql(expression, "expression")
    4018        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    4019        parameters = self.sql(expression, "params_struct")
    4020        return self.func("PREDICT", model, table, parameters or None)
    4021
    4022    def forin_sql(self, expression: exp.ForIn) -> str:
    4023        this = self.sql(expression, "this")
    4024        expression_sql = self.sql(expression, "expression")
    4025        return f"FOR {this} DO {expression_sql}"
    4026
    4027    def refresh_sql(self, expression: exp.Refresh) -> str:
    4028        this = self.sql(expression, "this")
    4029        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    4030        return f"REFRESH {table}{this}"
    4031
    4032    def toarray_sql(self, expression: exp.ToArray) -> str:
    4033        arg = expression.this
    4034        if not arg.type:
    4035            from sqlglot.optimizer.annotate_types import annotate_types
    4036
    4037            arg = annotate_types(arg, dialect=self.dialect)
    4038
    4039        if arg.is_type(exp.DataType.Type.ARRAY):
    4040            return self.sql(arg)
    4041
    4042        cond_for_null = arg.is_(exp.null())
    4043        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
    4044
    4045    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    4046        this = expression.this
    4047        time_format = self.format_time(expression)
    4048
    4049        if time_format:
    4050            return self.sql(
    4051                exp.cast(
    4052                    exp.StrToTime(this=this, format=expression.args["format"]),
    4053                    exp.DataType.Type.TIME,
    4054                )
    4055            )
    4056
    4057        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    4058            return self.sql(this)
    4059
    4060        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
    4061
    4062    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    4063        this = expression.this
    4064        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    4065            return self.sql(this)
    4066
    4067        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
    4068
    4069    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    4070        this = expression.this
    4071        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    4072            return self.sql(this)
    4073
    4074        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
    4075
    4076    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    4077        this = expression.this
    4078        time_format = self.format_time(expression)
    4079
    4080        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    4081            return self.sql(
    4082                exp.cast(
    4083                    exp.StrToTime(this=this, format=expression.args["format"]),
    4084                    exp.DataType.Type.DATE,
    4085                )
    4086            )
    4087
    4088        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    4089            return self.sql(this)
    4090
    4091        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
    4092
    4093    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    4094        return self.sql(
    4095            exp.func(
    4096                "DATEDIFF",
    4097                expression.this,
    4098                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    4099                "day",
    4100            )
    4101        )
    4102
    4103    def lastday_sql(self, expression: exp.LastDay) -> str:
    4104        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4105            return self.function_fallback_sql(expression)
    4106
    4107        unit = expression.text("unit")
    4108        if unit and unit != "MONTH":
    4109            self.unsupported("Date parts are not supported in LAST_DAY.")
    4110
    4111        return self.func("LAST_DAY", expression.this)
    4112
    4113    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4114        from sqlglot.dialects.dialect import unit_to_str
    4115
    4116        return self.func(
    4117            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4118        )
    4119
    4120    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4121        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4122            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4123            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4124            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4125            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4126
    4127        from sqlglot.dialects import Dialect
    4128
    4129        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4130        if self.dialect.__class__ != Dialect:
    4131            self.unsupported("ARRAY_ANY is unsupported")
    4132
    4133        return self.function_fallback_sql(expression)
    4134
    4135    def struct_sql(self, expression: exp.Struct) -> str:
    4136        expression.set(
    4137            "expressions",
    4138            [
    4139                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4140                if isinstance(e, exp.PropertyEQ)
    4141                else e
    4142                for e in expression.expressions
    4143            ],
    4144        )
    4145
    4146        return self.function_fallback_sql(expression)
    4147
    4148    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4149        low = self.sql(expression, "this")
    4150        high = self.sql(expression, "expression")
    4151
    4152        return f"{low} TO {high}"
    4153
    4154    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4155        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4156        tables = f" {self.expressions(expression)}"
    4157
    4158        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4159
    4160        on_cluster = self.sql(expression, "cluster")
    4161        on_cluster = f" {on_cluster}" if on_cluster else ""
    4162
    4163        identity = self.sql(expression, "identity")
    4164        identity = f" {identity} IDENTITY" if identity else ""
    4165
    4166        option = self.sql(expression, "option")
    4167        option = f" {option}" if option else ""
    4168
    4169        partition = self.sql(expression, "partition")
    4170        partition = f" {partition}" if partition else ""
    4171
    4172        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
    4173
    4174    # This transpiles T-SQL's CONVERT function
    4175    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
    4176    def convert_sql(self, expression: exp.Convert) -> str:
    4177        to = expression.this
    4178        value = expression.expression
    4179        style = expression.args.get("style")
    4180        safe = expression.args.get("safe")
    4181        strict = expression.args.get("strict")
    4182
    4183        if not to or not value:
    4184            return ""
    4185
    4186        # Retrieve length of datatype and override to default if not specified
    4187        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4188            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4189
    4190        transformed: t.Optional[exp.Expression] = None
    4191        cast = exp.Cast if strict else exp.TryCast
    4192
    4193        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4194        if isinstance(style, exp.Literal) and style.is_int:
    4195            from sqlglot.dialects.tsql import TSQL
    4196
    4197            style_value = style.name
    4198            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4199            if not converted_style:
    4200                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4201
    4202            fmt = exp.Literal.string(converted_style)
    4203
    4204            if to.this == exp.DataType.Type.DATE:
    4205                transformed = exp.StrToDate(this=value, format=fmt)
    4206            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4207                transformed = exp.StrToTime(this=value, format=fmt)
    4208            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4209                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4210            elif to.this == exp.DataType.Type.TEXT:
    4211                transformed = exp.TimeToStr(this=value, format=fmt)
    4212
    4213        if not transformed:
    4214            transformed = cast(this=value, to=to, safe=safe)
    4215
    4216        return self.sql(transformed)
    4217
    4218    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
    4219        this = expression.this
    4220        if isinstance(this, exp.JSONPathWildcard):
    4221            this = self.json_path_part(this)
    4222            return f".{this}" if this else ""
    4223
    4224        if exp.SAFE_IDENTIFIER_RE.match(this):
    4225            return f".{this}"
    4226
    4227        this = self.json_path_part(this)
    4228        return (
    4229            f"[{this}]"
    4230            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
    4231            else f".{this}"
    4232        )
    4233
    4234    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
    4235        this = self.json_path_part(expression.this)
    4236        return f"[{this}]" if this else ""
    4237
    4238    def _simplify_unless_literal(self, expression: E) -> E:
    4239        if not isinstance(expression, exp.Literal):
    4240            from sqlglot.optimizer.simplify import simplify
    4241
    4242            expression = simplify(expression, dialect=self.dialect)
    4243
    4244        return expression
    4245
    4246    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
    4247        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
    4248            # The first modifier here will be the one closest to the AggFunc's arg
    4249            mods = sorted(
    4250                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
    4251                key=lambda x: 0
    4252                if isinstance(x, exp.HavingMax)
    4253                else (1 if isinstance(x, exp.Order) else 2),
    4254            )
    4255
    4256            if mods:
    4257                mod = mods[0]
    4258                this = expression.__class__(this=mod.this.copy())
    4259                this.meta["inline"] = True
    4260                mod.this.replace(this)
    4261                return self.sql(expression.this)
    4262
    4263            agg_func = expression.find(exp.AggFunc)
    4264
    4265            if agg_func:
    4266                return self.sql(agg_func)[:-1] + f" {text})"
    4267
    4268        return f"{self.sql(expression, 'this')} {text}"
    4269
    4270    def _replace_line_breaks(self, string: str) -> str:
    4271        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
    4272        if self.pretty:
    4273            return string.replace("\n", self.SENTINEL_LINE_BREAK)
    4274        return string
    4275
    4276    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4277        option = self.sql(expression, "this")
    4278
    4279        if expression.expressions:
    4280            upper = option.upper()
    4281
    4282            # Snowflake FILE_FORMAT options are separated by whitespace
    4283            sep = " " if upper == "FILE_FORMAT" else ", "
    4284
    4285            # Databricks copy/format options do not set their list of values with EQ
    4286            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4287            values = self.expressions(expression, flat=True, sep=sep)
    4288            return f"{option}{op}({values})"
    4289
    4290        value = self.sql(expression, "expression")
    4291
    4292        if not value:
    4293            return option
    4294
    4295        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4296
    4297        return f"{option}{op}{value}"
    4298
    4299    def credentials_sql(self, expression: exp.Credentials) -> str:
    4300        cred_expr = expression.args.get("credentials")
    4301        if isinstance(cred_expr, exp.Literal):
    4302            # Redshift case: CREDENTIALS <string>
    4303            credentials = self.sql(expression, "credentials")
    4304            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4305        else:
    4306            # Snowflake case: CREDENTIALS = (...)
    4307            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4308            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4309
    4310        storage = self.sql(expression, "storage")
    4311        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4312
    4313        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4314        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4315
    4316        iam_role = self.sql(expression, "iam_role")
    4317        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4318
    4319        region = self.sql(expression, "region")
    4320        region = f" REGION {region}" if region else ""
    4321
    4322        return f"{credentials}{storage}{encryption}{iam_role}{region}"
    4323
    4324    def copy_sql(self, expression: exp.Copy) -> str:
    4325        this = self.sql(expression, "this")
    4326        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4327
    4328        credentials = self.sql(expression, "credentials")
    4329        credentials = self.seg(credentials) if credentials else ""
    4330        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    4331        files = self.expressions(expression, key="files", flat=True)
    4332
    4333        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4334        params = self.expressions(
    4335            expression,
    4336            key="params",
    4337            sep=sep,
    4338            new_line=True,
    4339            skip_last=True,
    4340            skip_first=True,
    4341            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4342        )
    4343
    4344        if params:
    4345            if self.COPY_PARAMS_ARE_WRAPPED:
    4346                params = f" WITH ({params})"
    4347            elif not self.pretty:
    4348                params = f" {params}"
    4349
    4350        return f"COPY{this}{kind} {files}{credentials}{params}"
    4351
    4352    def semicolon_sql(self, expression: exp.Semicolon) -> str:
    4353        return ""
    4354
    4355    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4356        on_sql = "ON" if expression.args.get("on") else "OFF"
    4357        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4358        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4359        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4360        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4361
    4362        if filter_col or retention_period:
    4363            on_sql = self.func("ON", filter_col, retention_period)
    4364
    4365        return f"DATA_DELETION={on_sql}"
    4366
    4367    def maskingpolicycolumnconstraint_sql(
    4368        self, expression: exp.MaskingPolicyColumnConstraint
    4369    ) -> str:
    4370        this = self.sql(expression, "this")
    4371        expressions = self.expressions(expression, flat=True)
    4372        expressions = f" USING ({expressions})" if expressions else ""
    4373        return f"MASKING POLICY {this}{expressions}"
    4374
    4375    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4376        this = self.sql(expression, "this")
    4377        this = f"TABLE {this}"
    4378        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
    4379
    4380    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    4381        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
    4382
    4383    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4384        this = self.sql(expression, "this")
    4385        expr = expression.expression
    4386
    4387        if isinstance(expr, exp.Func):
    4388            # T-SQL's CLR functions are case sensitive
    4389            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4390        else:
    4391            expr = self.sql(expression, "expression")
    4392
    4393        return self.scope_resolution(expr, this)
    4394
    4395    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4396        if self.PARSE_JSON_NAME is None:
    4397            return self.sql(expression.this)
    4398
    4399        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
    4400
    4401    def rand_sql(self, expression: exp.Rand) -> str:
    4402        lower = self.sql(expression, "lower")
    4403        upper = self.sql(expression, "upper")
    4404
    4405        if lower and upper:
    4406            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4407        return self.func("RAND", expression.this)
    4408
    4409    def changes_sql(self, expression: exp.Changes) -> str:
    4410        information = self.sql(expression, "information")
    4411        information = f"INFORMATION => {information}"
    4412        at_before = self.sql(expression, "at_before")
    4413        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4414        end = self.sql(expression, "end")
    4415        end = f"{self.seg('')}{end}" if end else ""
    4416
    4417        return f"CHANGES ({information}){at_before}{end}"
    4418
    4419    def pad_sql(self, expression: exp.Pad) -> str:
    4420        prefix = "L" if expression.args.get("is_left") else "R"
    4421
    4422        fill_pattern = self.sql(expression, "fill_pattern") or None
    4423        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4424            fill_pattern = "' '"
    4425
    4426        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
    4427
    4428    def summarize_sql(self, expression: exp.Summarize) -> str:
    4429        table = " TABLE" if expression.args.get("table") else ""
    4430        return f"SUMMARIZE{table} {self.sql(expression.this)}"
    4431
    4432    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4433        generate_series = exp.GenerateSeries(**expression.args)
    4434
    4435        parent = expression.parent
    4436        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4437            parent = parent.parent
    4438
    4439        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4440            return self.sql(exp.Unnest(expressions=[generate_series]))
    4441
    4442        if isinstance(parent, exp.Select):
    4443            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4444
    4445        return self.sql(generate_series)
    4446
    4447    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4448        exprs = expression.expressions
    4449        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4450            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4451        else:
    4452            rhs = self.expressions(expression)
    4453
    4454        return self.func(name, expression.this, rhs or None)
    4455
    4456    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4457        if self.SUPPORTS_CONVERT_TIMEZONE:
    4458            return self.function_fallback_sql(expression)
    4459
    4460        source_tz = expression.args.get("source_tz")
    4461        target_tz = expression.args.get("target_tz")
    4462        timestamp = expression.args.get("timestamp")
    4463
    4464        if source_tz and timestamp:
    4465            timestamp = exp.AtTimeZone(
    4466                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4467            )
    4468
    4469        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4470
    4471        return self.sql(expr)
    4472
    4473    def json_sql(self, expression: exp.JSON) -> str:
    4474        this = self.sql(expression, "this")
    4475        this = f" {this}" if this else ""
    4476
    4477        _with = expression.args.get("with")
    4478
    4479        if _with is None:
    4480            with_sql = ""
    4481        elif not _with:
    4482            with_sql = " WITHOUT"
    4483        else:
    4484            with_sql = " WITH"
    4485
    4486        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4487
    4488        return f"JSON{this}{with_sql}{unique_sql}"
    4489
    4490    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4491        def _generate_on_options(arg: t.Any) -> str:
    4492            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4493
    4494        path = self.sql(expression, "path")
    4495        returning = self.sql(expression, "returning")
    4496        returning = f" RETURNING {returning}" if returning else ""
    4497
    4498        on_condition = self.sql(expression, "on_condition")
    4499        on_condition = f" {on_condition}" if on_condition else ""
    4500
    4501        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
    4502
    4503    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4504        else_ = "ELSE " if expression.args.get("else_") else ""
    4505        condition = self.sql(expression, "expression")
    4506        condition = f"WHEN {condition} THEN " if condition else else_
    4507        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4508        return f"{condition}{insert}"
    4509
    4510    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4511        kind = self.sql(expression, "kind")
    4512        expressions = self.seg(self.expressions(expression, sep=" "))
    4513        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4514        return res
    4515
    4516    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4517        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4518        empty = expression.args.get("empty")
    4519        empty = (
    4520            f"DEFAULT {empty} ON EMPTY"
    4521            if isinstance(empty, exp.Expression)
    4522            else self.sql(expression, "empty")
    4523        )
    4524
    4525        error = expression.args.get("error")
    4526        error = (
    4527            f"DEFAULT {error} ON ERROR"
    4528            if isinstance(error, exp.Expression)
    4529            else self.sql(expression, "error")
    4530        )
    4531
    4532        if error and empty:
    4533            error = (
    4534                f"{empty} {error}"
    4535                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4536                else f"{error} {empty}"
    4537            )
    4538            empty = ""
    4539
    4540        null = self.sql(expression, "null")
    4541
    4542        return f"{empty}{error}{null}"
    4543
    4544    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
    4545        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
    4546        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
    4547
    4548    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4549        this = self.sql(expression, "this")
    4550        path = self.sql(expression, "path")
    4551
    4552        passing = self.expressions(expression, "passing")
    4553        passing = f" PASSING {passing}" if passing else ""
    4554
    4555        on_condition = self.sql(expression, "on_condition")
    4556        on_condition = f" {on_condition}" if on_condition else ""
    4557
    4558        path = f"{path}{passing}{on_condition}"
    4559
    4560        return self.func("JSON_EXISTS", this, path)
    4561
    4562    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4563        array_agg = self.function_fallback_sql(expression)
    4564
    4565        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4566        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4567        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4568            parent = expression.parent
    4569            if isinstance(parent, exp.Filter):
    4570                parent_cond = parent.expression.this
    4571                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4572            else:
    4573                this = expression.this
    4574                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4575                if this.find(exp.Column):
    4576                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4577                    this_sql = (
    4578                        self.expressions(this)
    4579                        if isinstance(this, exp.Distinct)
    4580                        else self.sql(expression, "this")
    4581                    )
    4582
    4583                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4584
    4585        return array_agg
    4586
    4587    def apply_sql(self, expression: exp.Apply) -> str:
    4588        this = self.sql(expression, "this")
    4589        expr = self.sql(expression, "expression")
    4590
    4591        return f"{this} APPLY({expr})"
    4592
    4593    def grant_sql(self, expression: exp.Grant) -> str:
    4594        privileges_sql = self.expressions(expression, key="privileges", flat=True)
    4595
    4596        kind = self.sql(expression, "kind")
    4597        kind = f" {kind}" if kind else ""
    4598
    4599        securable = self.sql(expression, "securable")
    4600        securable = f" {securable}" if securable else ""
    4601
    4602        principals = self.expressions(expression, key="principals", flat=True)
    4603
    4604        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
    4605
    4606        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
    4607
    4608    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4609        this = self.sql(expression, "this")
    4610        columns = self.expressions(expression, flat=True)
    4611        columns = f"({columns})" if columns else ""
    4612
    4613        return f"{this}{columns}"
    4614
    4615    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4616        this = self.sql(expression, "this")
    4617
    4618        kind = self.sql(expression, "kind")
    4619        kind = f"{kind} " if kind else ""
    4620
    4621        return f"{kind}{this}"
    4622
    4623    def columns_sql(self, expression: exp.Columns):
    4624        func = self.function_fallback_sql(expression)
    4625        if expression.args.get("unpack"):
    4626            func = f"*{func}"
    4627
    4628        return func
    4629
    4630    def overlay_sql(self, expression: exp.Overlay):
    4631        this = self.sql(expression, "this")
    4632        expr = self.sql(expression, "expression")
    4633        from_sql = self.sql(expression, "from")
    4634        for_sql = self.sql(expression, "for")
    4635        for_sql = f" FOR {for_sql}" if for_sql else ""
    4636
    4637        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
    4638
    4639    @unsupported_args("format")
    4640    def todouble_sql(self, expression: exp.ToDouble) -> str:
    4641        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    4642
    4643    def string_sql(self, expression: exp.String) -> str:
    4644        this = expression.this
    4645        zone = expression.args.get("zone")
    4646
    4647        if zone:
    4648            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4649            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4650            # set for source_tz to transpile the time conversion before the STRING cast
    4651            this = exp.ConvertTimezone(
    4652                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4653            )
    4654
    4655        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
    4656
    4657    def median_sql(self, expression: exp.Median):
    4658        if not self.SUPPORTS_MEDIAN:
    4659            return self.sql(
    4660                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4661            )
    4662
    4663        return self.function_fallback_sql(expression)
    4664
    4665    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4666        filler = self.sql(expression, "this")
    4667        filler = f" {filler}" if filler else ""
    4668        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4669        return f"TRUNCATE{filler} {with_count}"
    4670
    4671    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4672        if self.SUPPORTS_UNIX_SECONDS:
    4673            return self.function_fallback_sql(expression)
    4674
    4675        start_ts = exp.cast(
    4676            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4677        )
    4678
    4679        return self.sql(
    4680            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    4681        )
    4682
    4683    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    4684        dim = expression.expression
    4685
    4686        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    4687        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    4688            if not (dim.is_int and dim.name == "1"):
    4689                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    4690            dim = None
    4691
    4692        # If dimension is required but not specified, default initialize it
    4693        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    4694            dim = exp.Literal.number(1)
    4695
    4696        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
    4697
    4698    def attach_sql(self, expression: exp.Attach) -> str:
    4699        this = self.sql(expression, "this")
    4700        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    4701        expressions = self.expressions(expression)
    4702        expressions = f" ({expressions})" if expressions else ""
    4703
    4704        return f"ATTACH{exists_sql} {this}{expressions}"
    4705
    4706    def detach_sql(self, expression: exp.Detach) -> str:
    4707        this = self.sql(expression, "this")
    4708        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    4709
    4710        return f"DETACH{exists_sql} {this}"
    4711
    4712    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    4713        this = self.sql(expression, "this")
    4714        value = self.sql(expression, "expression")
    4715        value = f" {value}" if value else ""
    4716        return f"{this}{value}"
    4717
    4718    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4719        this_sql = self.sql(expression, "this")
    4720        if isinstance(expression.this, exp.Table):
    4721            this_sql = f"TABLE {this_sql}"
    4722
    4723        return self.func(
    4724            "FEATURES_AT_TIME",
    4725            this_sql,
    4726            expression.args.get("time"),
    4727            expression.args.get("num_rows"),
    4728            expression.args.get("ignore_feature_nulls"),
    4729        )
    4730
    4731    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    4732        return (
    4733            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    4734        )
    4735
    4736    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    4737        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    4738        encode = f"{encode} {self.sql(expression, 'this')}"
    4739
    4740        properties = expression.args.get("properties")
    4741        if properties:
    4742            encode = f"{encode} {self.properties(properties)}"
    4743
    4744        return encode
    4745
    4746    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    4747        this = self.sql(expression, "this")
    4748        include = f"INCLUDE {this}"
    4749
    4750        column_def = self.sql(expression, "column_def")
    4751        if column_def:
    4752            include = f"{include} {column_def}"
    4753
    4754        alias = self.sql(expression, "alias")
    4755        if alias:
    4756            include = f"{include} AS {alias}"
    4757
    4758        return include
    4759
    4760    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
    4761        name = f"NAME {self.sql(expression, 'this')}"
    4762        return self.func("XMLELEMENT", name, *expression.expressions)
    4763
    4764    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    4765        partitions = self.expressions(expression, "partition_expressions")
    4766        create = self.expressions(expression, "create_expressions")
    4767        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
    4768
    4769    def partitionbyrangepropertydynamic_sql(
    4770        self, expression: exp.PartitionByRangePropertyDynamic
    4771    ) -> str:
    4772        start = self.sql(expression, "start")
    4773        end = self.sql(expression, "end")
    4774
    4775        every = expression.args["every"]
    4776        if isinstance(every, exp.Interval) and every.this.is_string:
    4777            every.this.replace(exp.Literal.number(every.name))
    4778
    4779        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
    4780
    4781    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    4782        name = self.sql(expression, "this")
    4783        values = self.expressions(expression, flat=True)
    4784
    4785        return f"NAME {name} VALUE {values}"
    4786
    4787    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    4788        kind = self.sql(expression, "kind")
    4789        sample = self.sql(expression, "sample")
    4790        return f"SAMPLE {sample} {kind}"
    4791
    4792    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    4793        kind = self.sql(expression, "kind")
    4794        option = self.sql(expression, "option")
    4795        option = f" {option}" if option else ""
    4796        this = self.sql(expression, "this")
    4797        this = f" {this}" if this else ""
    4798        columns = self.expressions(expression)
    4799        columns = f" {columns}" if columns else ""
    4800        return f"{kind}{option} STATISTICS{this}{columns}"
    4801
    4802    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    4803        this = self.sql(expression, "this")
    4804        columns = self.expressions(expression)
    4805        inner_expression = self.sql(expression, "expression")
    4806        inner_expression = f" {inner_expression}" if inner_expression else ""
    4807        update_options = self.sql(expression, "update_options")
    4808        update_options = f" {update_options} UPDATE" if update_options else ""
    4809        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
    4810
    4811    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    4812        kind = self.sql(expression, "kind")
    4813        kind = f" {kind}" if kind else ""
    4814        return f"DELETE{kind} STATISTICS"
    4815
    4816    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
    4817        inner_expression = self.sql(expression, "expression")
    4818        return f"LIST CHAINED ROWS{inner_expression}"
    4819
    4820    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    4821        kind = self.sql(expression, "kind")
    4822        this = self.sql(expression, "this")
    4823        this = f" {this}" if this else ""
    4824        inner_expression = self.sql(expression, "expression")
    4825        return f"VALIDATE {kind}{this}{inner_expression}"
    4826
    4827    def analyze_sql(self, expression: exp.Analyze) -> str:
    4828        options = self.expressions(expression, key="options", sep=" ")
    4829        options = f" {options}" if options else ""
    4830        kind = self.sql(expression, "kind")
    4831        kind = f" {kind}" if kind else ""
    4832        this = self.sql(expression, "this")
    4833        this = f" {this}" if this else ""
    4834        mode = self.sql(expression, "mode")
    4835        mode = f" {mode}" if mode else ""
    4836        properties = self.sql(expression, "properties")
    4837        properties = f" {properties}" if properties else ""
    4838        partition = self.sql(expression, "partition")
    4839        partition = f" {partition}" if partition else ""
    4840        inner_expression = self.sql(expression, "expression")
    4841        inner_expression = f" {inner_expression}" if inner_expression else ""
    4842        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
    4843
    4844    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    4845        this = self.sql(expression, "this")
    4846        namespaces = self.expressions(expression, key="namespaces")
    4847        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    4848        passing = self.expressions(expression, key="passing")
    4849        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    4850        columns = self.expressions(expression, key="columns")
    4851        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    4852        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    4853        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
    4854
    4855    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
    4856        this = self.sql(expression, "this")
    4857        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
    4858
    4859    def export_sql(self, expression: exp.Export) -> str:
    4860        this = self.sql(expression, "this")
    4861        connection = self.sql(expression, "connection")
    4862        connection = f"WITH CONNECTION {connection} " if connection else ""
    4863        options = self.sql(expression, "options")
    4864        return f"EXPORT DATA {connection}{options} AS {this}"
    4865
    4866    def declare_sql(self, expression: exp.Declare) -> str:
    4867        return f"DECLARE {self.expressions(expression, flat=True)}"
    4868
    4869    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
    4870        variable = self.sql(expression, "this")
    4871        default = self.sql(expression, "default")
    4872        default = f" = {default}" if default else ""
    4873
    4874        kind = self.sql(expression, "kind")
    4875        if isinstance(expression.args.get("kind"), exp.Schema):
    4876            kind = f"TABLE {kind}"
    4877
    4878        return f"{variable} AS {kind}{default}"
    4879
    4880    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
    4881        kind = self.sql(expression, "kind")
    4882        this = self.sql(expression, "this")
    4883        set = self.sql(expression, "expression")
    4884        using = self.sql(expression, "using")
    4885        using = f" USING {using}" if using else ""
    4886
    4887        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
    4888
    4889        return f"{kind_sql} {this} SET {set}{using}"
    4890
    4891    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
    4892        params = self.expressions(expression, key="params", flat=True)
    4893        return self.func(expression.name, *expression.expressions) + f"({params})"
    4894
    4895    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
    4896        return self.func(expression.name, *expression.expressions)
    4897
    4898    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
    4899        return self.anonymousaggfunc_sql(expression)
    4900
    4901    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
    4902        return self.parameterizedagg_sql(expression)
    4903
    4904    def show_sql(self, expression: exp.Show) -> str:
    4905        self.unsupported("Unsupported SHOW statement")
    4906        return ""
    4907
    4908    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
    4909        # Snowflake GET/PUT statements:
    4910        #   PUT <file> <internalStage> <properties>
    4911        #   GET <internalStage> <file> <properties>
    4912        props = expression.args.get("properties")
    4913        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
    4914        this = self.sql(expression, "this")
    4915        target = self.sql(expression, "target")
    4916
    4917        if isinstance(expression, exp.Put):
    4918            return f"PUT {this} {target}{props_sql}"
    4919        else:
    4920            return f"GET {target} {this}{props_sql}"

logger = <Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE = re.compile('\\\\(\\d+)')
UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
def unsupported_args( *args: Union[str, Tuple[str, str]]) -> Callable[[Callable[[~G, ~E], str]], Callable[[~G, ~E], str]]: View Source

    30def unsupported_args(
    31    *args: t.Union[str, t.Tuple[str, str]],
    32) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
    33    """
    34    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
    35    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
    36    """
    37    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
    38    for arg in args:
    39        if isinstance(arg, str):
    40            diagnostic_by_arg[arg] = None
    41        else:
    42            diagnostic_by_arg[arg[0]] = arg[1]
    43
    44    def decorator(func: GeneratorMethod) -> GeneratorMethod:
    45        @wraps(func)
    46        def _func(generator: G, expression: E) -> str:
    47            expression_name = expression.__class__.__name__
    48            dialect_name = generator.dialect.__class__.__name__
    49
    50            for arg_name, diagnostic in diagnostic_by_arg.items():
    51                if expression.args.get(arg_name):
    52                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
    53                        arg_name, expression_name, dialect_name
    54                    )
    55                    generator.unsupported(diagnostic)
    56
    57            return func(generator, expression)
    58
    59        return _func
    60
    61    return decorator

Decorator that can be used to mark certain args of an `Expression` subclass as unsupported. It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).

class Generator: View Source

      75class Generator(metaclass=_Generator):
      76    """
      77    Generator converts a given syntax tree to the corresponding SQL string.
      78
      79    Args:
      80        pretty: Whether to format the produced SQL string.
      81            Default: False.
      82        identify: Determines when an identifier should be quoted. Possible values are:
      83            False (default): Never quote, except in cases where it's mandatory by the dialect.
      84            True or 'always': Always quote.
      85            'safe': Only quote identifiers that are case insensitive.
      86        normalize: Whether to normalize identifiers to lowercase.
      87            Default: False.
      88        pad: The pad size in a formatted string. For example, this affects the indentation of
      89            a projection in a query, relative to its nesting level.
      90            Default: 2.
      91        indent: The indentation size in a formatted string. For example, this affects the
      92            indentation of subqueries and filters under a `WHERE` clause.
      93            Default: 2.
      94        normalize_functions: How to normalize function names. Possible values are:
      95            "upper" or True (default): Convert names to uppercase.
      96            "lower": Convert names to lowercase.
      97            False: Disables function name normalization.
      98        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
      99            Default ErrorLevel.WARN.
     100        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
     101            This is only relevant if unsupported_level is ErrorLevel.RAISE.
     102            Default: 3
     103        leading_comma: Whether the comma is leading or trailing in select expressions.
     104            This is only relevant when generating in pretty mode.
     105            Default: False
     106        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
     107            The default is on the smaller end because the length only represents a segment and not the true
     108            line length.
     109            Default: 80
     110        comments: Whether to preserve comments in the output SQL code.
     111            Default: True
     112    """
     113
     114    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
     115        **JSON_PATH_PART_TRANSFORMS,
     116        exp.AllowedValuesProperty: lambda self,
     117        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
     118        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
     119        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
     120        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
     121        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
     122        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
     123        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
     124        exp.CaseSpecificColumnConstraint: lambda _,
     125        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
     126        exp.Ceil: lambda self, e: self.ceil_floor(e),
     127        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
     128        exp.CharacterSetProperty: lambda self,
     129        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
     130        exp.ClusteredColumnConstraint: lambda self,
     131        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
     132        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
     133        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
     134        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
     135        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
     136        exp.CredentialsProperty: lambda self,
     137        e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
     138        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
     139        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
     140        exp.DynamicProperty: lambda *_: "DYNAMIC",
     141        exp.EmptyProperty: lambda *_: "EMPTY",
     142        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
     143        exp.EphemeralColumnConstraint: lambda self,
     144        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
     145        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
     146        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
     147        exp.Except: lambda self, e: self.set_operations(e),
     148        exp.ExternalProperty: lambda *_: "EXTERNAL",
     149        exp.Floor: lambda self, e: self.ceil_floor(e),
     150        exp.Get: lambda self, e: self.get_put_sql(e),
     151        exp.GlobalProperty: lambda *_: "GLOBAL",
     152        exp.HeapProperty: lambda *_: "HEAP",
     153        exp.IcebergProperty: lambda *_: "ICEBERG",
     154        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
     155        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
     156        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
     157        exp.Intersect: lambda self, e: self.set_operations(e),
     158        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
     159        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
     160        exp.LanguageProperty: lambda self, e: self.naked_property(e),
     161        exp.LocationProperty: lambda self, e: self.naked_property(e),
     162        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
     163        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
     164        exp.NonClusteredColumnConstraint: lambda self,
     165        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
     166        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
     167        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
     168        exp.OnCommitProperty: lambda _,
     169        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
     170        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
     171        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
     172        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
     173        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
     174        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
     175        exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
     176        exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
     177        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
     178        exp.ProjectionPolicyColumnConstraint: lambda self,
     179        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
     180        exp.Put: lambda self, e: self.get_put_sql(e),
     181        exp.RemoteWithConnectionModelProperty: lambda self,
     182        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
     183        exp.ReturnsProperty: lambda self, e: (
     184            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
     185        ),
     186        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
     187        exp.SecureProperty: lambda *_: "SECURE",
     188        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
     189        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
     190        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
     191        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
     192        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
     193        exp.SqlReadWriteProperty: lambda _, e: e.name,
     194        exp.SqlSecurityProperty: lambda _,
     195        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
     196        exp.StabilityProperty: lambda _, e: e.name,
     197        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
     198        exp.StreamingTableProperty: lambda *_: "STREAMING",
     199        exp.StrictProperty: lambda *_: "STRICT",
     200        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
     201        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
     202        exp.TemporaryProperty: lambda *_: "TEMPORARY",
     203        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     204        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
     205        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
     206        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
     207        exp.TransientProperty: lambda *_: "TRANSIENT",
     208        exp.Union: lambda self, e: self.set_operations(e),
     209        exp.UnloggedProperty: lambda *_: "UNLOGGED",
     210        exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
     211        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
     212        exp.Uuid: lambda *_: "UUID()",
     213        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
     214        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
     215        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
     216        exp.VolatileProperty: lambda *_: "VOLATILE",
     217        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
     218        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
     219        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
     220        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
     221        exp.ForceProperty: lambda *_: "FORCE",
     222    }
     223
     224    # Whether null ordering is supported in order by
     225    # True: Full Support, None: No support, False: No support for certain cases
     226    # such as window specifications, aggregate functions etc
     227    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
     228
     229    # Whether ignore nulls is inside the agg or outside.
     230    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
     231    IGNORE_NULLS_IN_FUNC = False
     232
     233    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     234    LOCKING_READS_SUPPORTED = False
     235
     236    # Whether the EXCEPT and INTERSECT operations can return duplicates
     237    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
     238
     239    # Wrap derived values in parens, usually standard but spark doesn't support it
     240    WRAP_DERIVED_VALUES = True
     241
     242    # Whether create function uses an AS before the RETURN
     243    CREATE_FUNCTION_RETURN_AS = True
     244
     245    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
     246    MATCHED_BY_SOURCE = True
     247
     248    # Whether the INTERVAL expression works only with values like '1 day'
     249    SINGLE_STRING_INTERVAL = False
     250
     251    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     252    INTERVAL_ALLOWS_PLURAL_FORM = True
     253
     254    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     255    LIMIT_FETCH = "ALL"
     256
     257    # Whether limit and fetch allows expresions or just limits
     258    LIMIT_ONLY_LITERALS = False
     259
     260    # Whether a table is allowed to be renamed with a db
     261    RENAME_TABLE_WITH_DB = True
     262
     263    # The separator for grouping sets and rollups
     264    GROUPINGS_SEP = ","
     265
     266    # The string used for creating an index on a table
     267    INDEX_ON = "ON"
     268
     269    # Whether join hints should be generated
     270    JOIN_HINTS = True
     271
     272    # Whether table hints should be generated
     273    TABLE_HINTS = True
     274
     275    # Whether query hints should be generated
     276    QUERY_HINTS = True
     277
     278    # What kind of separator to use for query hints
     279    QUERY_HINT_SEP = ", "
     280
     281    # Whether comparing against booleans (e.g. x IS TRUE) is supported
     282    IS_BOOL_ALLOWED = True
     283
     284    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
     285    DUPLICATE_KEY_UPDATE_WITH_SET = True
     286
     287    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
     288    LIMIT_IS_TOP = False
     289
     290    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
     291    RETURNING_END = True
     292
     293    # Whether to generate an unquoted value for EXTRACT's date part argument
     294    EXTRACT_ALLOWS_QUOTES = True
     295
     296    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
     297    TZ_TO_WITH_TIME_ZONE = False
     298
     299    # Whether the NVL2 function is supported
     300    NVL2_SUPPORTED = True
     301
     302    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
     303    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
     304
     305    # Whether VALUES statements can be used as derived tables.
     306    # MySQL 5 and Redshift do not allow this, so when False, it will convert
     307    # SELECT * VALUES into SELECT UNION
     308    VALUES_AS_TABLE = True
     309
     310    # Whether the word COLUMN is included when adding a column with ALTER TABLE
     311    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
     312
     313    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     314    UNNEST_WITH_ORDINALITY = True
     315
     316    # Whether FILTER (WHERE cond) can be used for conditional aggregation
     317    AGGREGATE_FILTER_SUPPORTED = True
     318
     319    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
     320    SEMI_ANTI_JOIN_WITH_SIDE = True
     321
     322    # Whether to include the type of a computed column in the CREATE DDL
     323    COMPUTED_COLUMN_WITH_TYPE = True
     324
     325    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
     326    SUPPORTS_TABLE_COPY = True
     327
     328    # Whether parentheses are required around the table sample's expression
     329    TABLESAMPLE_REQUIRES_PARENS = True
     330
     331    # Whether a table sample clause's size needs to be followed by the ROWS keyword
     332    TABLESAMPLE_SIZE_IS_ROWS = True
     333
     334    # The keyword(s) to use when generating a sample clause
     335    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
     336
     337    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
     338    TABLESAMPLE_WITH_METHOD = True
     339
     340    # The keyword to use when specifying the seed of a sample clause
     341    TABLESAMPLE_SEED_KEYWORD = "SEED"
     342
     343    # Whether COLLATE is a function instead of a binary operator
     344    COLLATE_IS_FUNC = False
     345
     346    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
     347    DATA_TYPE_SPECIFIERS_ALLOWED = False
     348
     349    # Whether conditions require booleans WHERE x = 0 vs WHERE x
     350    ENSURE_BOOLS = False
     351
     352    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
     353    CTE_RECURSIVE_KEYWORD_REQUIRED = True
     354
     355    # Whether CONCAT requires >1 arguments
     356    SUPPORTS_SINGLE_ARG_CONCAT = True
     357
     358    # Whether LAST_DAY function supports a date part argument
     359    LAST_DAY_SUPPORTS_DATE_PART = True
     360
     361    # Whether named columns are allowed in table aliases
     362    SUPPORTS_TABLE_ALIAS_COLUMNS = True
     363
     364    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
     365    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
     366
     367    # What delimiter to use for separating JSON key/value pairs
     368    JSON_KEY_VALUE_PAIR_SEP = ":"
     369
     370    # INSERT OVERWRITE TABLE x override
     371    INSERT_OVERWRITE = " OVERWRITE TABLE"
     372
     373    # Whether the SELECT .. INTO syntax is used instead of CTAS
     374    SUPPORTS_SELECT_INTO = False
     375
     376    # Whether UNLOGGED tables can be created
     377    SUPPORTS_UNLOGGED_TABLES = False
     378
     379    # Whether the CREATE TABLE LIKE statement is supported
     380    SUPPORTS_CREATE_TABLE_LIKE = True
     381
     382    # Whether the LikeProperty needs to be specified inside of the schema clause
     383    LIKE_PROPERTY_INSIDE_SCHEMA = False
     384
     385    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
     386    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
     387    MULTI_ARG_DISTINCT = True
     388
     389    # Whether the JSON extraction operators expect a value of type JSON
     390    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
     391
     392    # Whether bracketed keys like ["foo"] are supported in JSON paths
     393    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
     394
     395    # Whether to escape keys using single quotes in JSON paths
     396    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
     397
     398    # The JSONPathPart expressions supported by this dialect
     399    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
     400
     401    # Whether any(f(x) for x in array) can be implemented by this dialect
     402    CAN_IMPLEMENT_ARRAY_ANY = False
     403
     404    # Whether the function TO_NUMBER is supported
     405    SUPPORTS_TO_NUMBER = True
     406
     407    # Whether or not set op modifiers apply to the outer set op or select.
     408    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
     409    # True means limit 1 happens after the set op, False means it it happens on y.
     410    SET_OP_MODIFIERS = True
     411
     412    # Whether parameters from COPY statement are wrapped in parentheses
     413    COPY_PARAMS_ARE_WRAPPED = True
     414
     415    # Whether values of params are set with "=" token or empty space
     416    COPY_PARAMS_EQ_REQUIRED = False
     417
     418    # Whether COPY statement has INTO keyword
     419    COPY_HAS_INTO_KEYWORD = True
     420
     421    # Whether the conditional TRY(expression) function is supported
     422    TRY_SUPPORTED = True
     423
     424    # Whether the UESCAPE syntax in unicode strings is supported
     425    SUPPORTS_UESCAPE = True
     426
     427    # The keyword to use when generating a star projection with excluded columns
     428    STAR_EXCEPT = "EXCEPT"
     429
     430    # The HEX function name
     431    HEX_FUNC = "HEX"
     432
     433    # The keywords to use when prefixing & separating WITH based properties
     434    WITH_PROPERTIES_PREFIX = "WITH"
     435
     436    # Whether to quote the generated expression of exp.JsonPath
     437    QUOTE_JSON_PATH = True
     438
     439    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
     440    PAD_FILL_PATTERN_IS_REQUIRED = False
     441
     442    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
     443    SUPPORTS_EXPLODING_PROJECTIONS = True
     444
     445    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
     446    ARRAY_CONCAT_IS_VAR_LEN = True
     447
     448    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
     449    SUPPORTS_CONVERT_TIMEZONE = False
     450
     451    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
     452    SUPPORTS_MEDIAN = True
     453
     454    # Whether UNIX_SECONDS(timestamp) is supported
     455    SUPPORTS_UNIX_SECONDS = False
     456
     457    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
     458    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
     459
     460    # The function name of the exp.ArraySize expression
     461    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
     462
     463    # The syntax to use when altering the type of a column
     464    ALTER_SET_TYPE = "SET DATA TYPE"
     465
     466    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
     467    # None -> Doesn't support it at all
     468    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
     469    # True (Postgres) -> Explicitly requires it
     470    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
     471
     472    TYPE_MAPPING = {
     473        exp.DataType.Type.DATETIME2: "TIMESTAMP",
     474        exp.DataType.Type.NCHAR: "CHAR",
     475        exp.DataType.Type.NVARCHAR: "VARCHAR",
     476        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     477        exp.DataType.Type.LONGTEXT: "TEXT",
     478        exp.DataType.Type.TINYTEXT: "TEXT",
     479        exp.DataType.Type.BLOB: "VARBINARY",
     480        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     481        exp.DataType.Type.LONGBLOB: "BLOB",
     482        exp.DataType.Type.TINYBLOB: "BLOB",
     483        exp.DataType.Type.INET: "INET",
     484        exp.DataType.Type.ROWVERSION: "VARBINARY",
     485        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
     486    }
     487
     488    TIME_PART_SINGULARS = {
     489        "MICROSECONDS": "MICROSECOND",
     490        "SECONDS": "SECOND",
     491        "MINUTES": "MINUTE",
     492        "HOURS": "HOUR",
     493        "DAYS": "DAY",
     494        "WEEKS": "WEEK",
     495        "MONTHS": "MONTH",
     496        "QUARTERS": "QUARTER",
     497        "YEARS": "YEAR",
     498    }
     499
     500    AFTER_HAVING_MODIFIER_TRANSFORMS = {
     501        "cluster": lambda self, e: self.sql(e, "cluster"),
     502        "distribute": lambda self, e: self.sql(e, "distribute"),
     503        "sort": lambda self, e: self.sql(e, "sort"),
     504        "windows": lambda self, e: (
     505            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
     506            if e.args.get("windows")
     507            else ""
     508        ),
     509        "qualify": lambda self, e: self.sql(e, "qualify"),
     510    }
     511
     512    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     513
     514    STRUCT_DELIMITER = ("<", ">")
     515
     516    PARAMETER_TOKEN = "@"
     517    NAMED_PLACEHOLDER_TOKEN = ":"
     518
     519    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
     520
     521    PROPERTIES_LOCATION = {
     522        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
     523        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     524        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     525        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
     526        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
     527        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     528        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     529        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     530        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     531        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
     532        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     533        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
     534        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
     535        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
     536        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     537        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
     538        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     539        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     540        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     541        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
     542        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     543        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     544        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
     545        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
     546        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     547        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     548        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     549        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     550        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     551        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     552        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
     553        exp.HeapProperty: exp.Properties.Location.POST_WITH,
     554        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
     555        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
     556        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
     557        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
     558        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     559        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     560        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     561        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     562        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     563        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
     564        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     565        exp.LogProperty: exp.Properties.Location.POST_NAME,
     566        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     567        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     568        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     569        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
     570        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     571        exp.Order: exp.Properties.Location.POST_SCHEMA,
     572        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
     573        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     574        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
     575        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     576        exp.Property: exp.Properties.Location.POST_WITH,
     577        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
     578        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     579        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     580        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     581        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     582        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
     583        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     584        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
     585        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
     586        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     587        exp.Set: exp.Properties.Location.POST_SCHEMA,
     588        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     589        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     590        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
     591        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
     592        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
     593        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     594        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
     595        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     596        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     597        exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
     598        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
     599        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
     600        exp.Tags: exp.Properties.Location.POST_WITH,
     601        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     602        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
     603        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     604        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
     605        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     606        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
     607        exp.UsingTemplateProperty: exp.Properties.Location.POST_SCHEMA,
     608        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
     609        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     610        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     611        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     612        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
     613        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
     614        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
     615        exp.ForceProperty: exp.Properties.Location.POST_CREATE,
     616    }
     617
     618    # Keywords that can't be used as unquoted identifier names
     619    RESERVED_KEYWORDS: t.Set[str] = set()
     620
     621    # Expressions whose comments are separated from them for better formatting
     622    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     623        exp.Command,
     624        exp.Create,
     625        exp.Describe,
     626        exp.Delete,
     627        exp.Drop,
     628        exp.From,
     629        exp.Insert,
     630        exp.Join,
     631        exp.MultitableInserts,
     632        exp.Select,
     633        exp.SetOperation,
     634        exp.Update,
     635        exp.Where,
     636        exp.With,
     637    )
     638
     639    # Expressions that should not have their comments generated in maybe_comment
     640    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     641        exp.Binary,
     642        exp.SetOperation,
     643    )
     644
     645    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
     646    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
     647        exp.Column,
     648        exp.Literal,
     649        exp.Neg,
     650        exp.Paren,
     651    )
     652
     653    PARAMETERIZABLE_TEXT_TYPES = {
     654        exp.DataType.Type.NVARCHAR,
     655        exp.DataType.Type.VARCHAR,
     656        exp.DataType.Type.CHAR,
     657        exp.DataType.Type.NCHAR,
     658    }
     659
     660    # Expressions that need to have all CTEs under them bubbled up to them
     661    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
     662
     663    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     664
     665    __slots__ = (
     666        "pretty",
     667        "identify",
     668        "normalize",
     669        "pad",
     670        "_indent",
     671        "normalize_functions",
     672        "unsupported_level",
     673        "max_unsupported",
     674        "leading_comma",
     675        "max_text_width",
     676        "comments",
     677        "dialect",
     678        "unsupported_messages",
     679        "_escaped_quote_end",
     680        "_escaped_identifier_end",
     681        "_next_name",
     682        "_identifier_start",
     683        "_identifier_end",
     684        "_quote_json_path_key_using_brackets",
     685    )
     686
     687    def __init__(
     688        self,
     689        pretty: t.Optional[bool] = None,
     690        identify: str | bool = False,
     691        normalize: bool = False,
     692        pad: int = 2,
     693        indent: int = 2,
     694        normalize_functions: t.Optional[str | bool] = None,
     695        unsupported_level: ErrorLevel = ErrorLevel.WARN,
     696        max_unsupported: int = 3,
     697        leading_comma: bool = False,
     698        max_text_width: int = 80,
     699        comments: bool = True,
     700        dialect: DialectType = None,
     701    ):
     702        import sqlglot
     703        from sqlglot.dialects import Dialect
     704
     705        self.pretty = pretty if pretty is not None else sqlglot.pretty
     706        self.identify = identify
     707        self.normalize = normalize
     708        self.pad = pad
     709        self._indent = indent
     710        self.unsupported_level = unsupported_level
     711        self.max_unsupported = max_unsupported
     712        self.leading_comma = leading_comma
     713        self.max_text_width = max_text_width
     714        self.comments = comments
     715        self.dialect = Dialect.get_or_raise(dialect)
     716
     717        # This is both a Dialect property and a Generator argument, so we prioritize the latter
     718        self.normalize_functions = (
     719            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
     720        )
     721
     722        self.unsupported_messages: t.List[str] = []
     723        self._escaped_quote_end: str = (
     724            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
     725        )
     726        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
     727
     728        self._next_name = name_sequence("_t")
     729
     730        self._identifier_start = self.dialect.IDENTIFIER_START
     731        self._identifier_end = self.dialect.IDENTIFIER_END
     732
     733        self._quote_json_path_key_using_brackets = True
     734
     735    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
     736        """
     737        Generates the SQL string corresponding to the given syntax tree.
     738
     739        Args:
     740            expression: The syntax tree.
     741            copy: Whether to copy the expression. The generator performs mutations so
     742                it is safer to copy.
     743
     744        Returns:
     745            The SQL string corresponding to `expression`.
     746        """
     747        if copy:
     748            expression = expression.copy()
     749
     750        expression = self.preprocess(expression)
     751
     752        self.unsupported_messages = []
     753        sql = self.sql(expression).strip()
     754
     755        if self.pretty:
     756            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     757
     758        if self.unsupported_level == ErrorLevel.IGNORE:
     759            return sql
     760
     761        if self.unsupported_level == ErrorLevel.WARN:
     762            for msg in self.unsupported_messages:
     763                logger.warning(msg)
     764        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     765            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     766
     767        return sql
     768
     769    def preprocess(self, expression: exp.Expression) -> exp.Expression:
     770        """Apply generic preprocessing transformations to a given expression."""
     771        expression = self._move_ctes_to_top_level(expression)
     772
     773        if self.ENSURE_BOOLS:
     774            from sqlglot.transforms import ensure_bools
     775
     776            expression = ensure_bools(expression)
     777
     778        return expression
     779
     780    def _move_ctes_to_top_level(self, expression: E) -> E:
     781        if (
     782            not expression.parent
     783            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
     784            and any(node.parent is not expression for node in expression.find_all(exp.With))
     785        ):
     786            from sqlglot.transforms import move_ctes_to_top_level
     787
     788            expression = move_ctes_to_top_level(expression)
     789        return expression
     790
     791    def unsupported(self, message: str) -> None:
     792        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     793            raise UnsupportedError(message)
     794        self.unsupported_messages.append(message)
     795
     796    def sep(self, sep: str = " ") -> str:
     797        return f"{sep.strip()}\n" if self.pretty else sep
     798
     799    def seg(self, sql: str, sep: str = " ") -> str:
     800        return f"{self.sep(sep)}{sql}"
     801
     802    def pad_comment(self, comment: str) -> str:
     803        comment = " " + comment if comment[0].strip() else comment
     804        comment = comment + " " if comment[-1].strip() else comment
     805        return comment
     806
     807    def maybe_comment(
     808        self,
     809        sql: str,
     810        expression: t.Optional[exp.Expression] = None,
     811        comments: t.Optional[t.List[str]] = None,
     812        separated: bool = False,
     813    ) -> str:
     814        comments = (
     815            ((expression and expression.comments) if comments is None else comments)  # type: ignore
     816            if self.comments
     817            else None
     818        )
     819
     820        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
     821            return sql
     822
     823        comments_sql = " ".join(
     824            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     825        )
     826
     827        if not comments_sql:
     828            return sql
     829
     830        comments_sql = self._replace_line_breaks(comments_sql)
     831
     832        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     833            return (
     834                f"{self.sep()}{comments_sql}{sql}"
     835                if not sql or sql[0].isspace()
     836                else f"{comments_sql}{self.sep()}{sql}"
     837            )
     838
     839        return f"{sql} {comments_sql}"
     840
     841    def wrap(self, expression: exp.Expression | str) -> str:
     842        this_sql = (
     843            self.sql(expression)
     844            if isinstance(expression, exp.UNWRAPPED_QUERIES)
     845            else self.sql(expression, "this")
     846        )
     847        if not this_sql:
     848            return "()"
     849
     850        this_sql = self.indent(this_sql, level=1, pad=0)
     851        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     852
     853    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     854        original = self.identify
     855        self.identify = False
     856        result = func(*args, **kwargs)
     857        self.identify = original
     858        return result
     859
     860    def normalize_func(self, name: str) -> str:
     861        if self.normalize_functions == "upper" or self.normalize_functions is True:
     862            return name.upper()
     863        if self.normalize_functions == "lower":
     864            return name.lower()
     865        return name
     866
     867    def indent(
     868        self,
     869        sql: str,
     870        level: int = 0,
     871        pad: t.Optional[int] = None,
     872        skip_first: bool = False,
     873        skip_last: bool = False,
     874    ) -> str:
     875        if not self.pretty or not sql:
     876            return sql
     877
     878        pad = self.pad if pad is None else pad
     879        lines = sql.split("\n")
     880
     881        return "\n".join(
     882            (
     883                line
     884                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     885                else f"{' ' * (level * self._indent + pad)}{line}"
     886            )
     887            for i, line in enumerate(lines)
     888        )
     889
     890    def sql(
     891        self,
     892        expression: t.Optional[str | exp.Expression],
     893        key: t.Optional[str] = None,
     894        comment: bool = True,
     895    ) -> str:
     896        if not expression:
     897            return ""
     898
     899        if isinstance(expression, str):
     900            return expression
     901
     902        if key:
     903            value = expression.args.get(key)
     904            if value:
     905                return self.sql(value)
     906            return ""
     907
     908        transform = self.TRANSFORMS.get(expression.__class__)
     909
     910        if callable(transform):
     911            sql = transform(self, expression)
     912        elif isinstance(expression, exp.Expression):
     913            exp_handler_name = f"{expression.key}_sql"
     914
     915            if hasattr(self, exp_handler_name):
     916                sql = getattr(self, exp_handler_name)(expression)
     917            elif isinstance(expression, exp.Func):
     918                sql = self.function_fallback_sql(expression)
     919            elif isinstance(expression, exp.Property):
     920                sql = self.property_sql(expression)
     921            else:
     922                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     923        else:
     924            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     925
     926        return self.maybe_comment(sql, expression) if self.comments and comment else sql
     927
     928    def uncache_sql(self, expression: exp.Uncache) -> str:
     929        table = self.sql(expression, "this")
     930        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     931        return f"UNCACHE TABLE{exists_sql} {table}"
     932
     933    def cache_sql(self, expression: exp.Cache) -> str:
     934        lazy = " LAZY" if expression.args.get("lazy") else ""
     935        table = self.sql(expression, "this")
     936        options = expression.args.get("options")
     937        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     938        sql = self.sql(expression, "expression")
     939        sql = f" AS{self.sep()}{sql}" if sql else ""
     940        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     941        return self.prepend_ctes(expression, sql)
     942
     943    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     944        if isinstance(expression.parent, exp.Cast):
     945            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     946        default = "DEFAULT " if expression.args.get("default") else ""
     947        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     948
     949    def column_parts(self, expression: exp.Column) -> str:
     950        return ".".join(
     951            self.sql(part)
     952            for part in (
     953                expression.args.get("catalog"),
     954                expression.args.get("db"),
     955                expression.args.get("table"),
     956                expression.args.get("this"),
     957            )
     958            if part
     959        )
     960
     961    def column_sql(self, expression: exp.Column) -> str:
     962        join_mark = " (+)" if expression.args.get("join_mark") else ""
     963
     964        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
     965            join_mark = ""
     966            self.unsupported("Outer join syntax using the (+) operator is not supported.")
     967
     968        return f"{self.column_parts(expression)}{join_mark}"
     969
     970    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     971        this = self.sql(expression, "this")
     972        this = f" {this}" if this else ""
     973        position = self.sql(expression, "position")
     974        return f"{position}{this}"
     975
     976    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     977        column = self.sql(expression, "this")
     978        kind = self.sql(expression, "kind")
     979        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     980        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     981        kind = f"{sep}{kind}" if kind else ""
     982        constraints = f" {constraints}" if constraints else ""
     983        position = self.sql(expression, "position")
     984        position = f" {position}" if position else ""
     985
     986        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
     987            kind = ""
     988
     989        return f"{exists}{column}{kind}{constraints}{position}"
     990
     991    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     992        this = self.sql(expression, "this")
     993        kind_sql = self.sql(expression, "kind").strip()
     994        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     995
     996    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     997        this = self.sql(expression, "this")
     998        if expression.args.get("not_null"):
     999            persisted = " PERSISTED NOT NULL"
    1000        elif expression.args.get("persisted"):
    1001            persisted = " PERSISTED"
    1002        else:
    1003            persisted = ""
    1004        return f"AS {this}{persisted}"
    1005
    1006    def autoincrementcolumnconstraint_sql(self, _) -> str:
    1007        return self.token_sql(TokenType.AUTO_INCREMENT)
    1008
    1009    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    1010        if isinstance(expression.this, list):
    1011            this = self.wrap(self.expressions(expression, key="this", flat=True))
    1012        else:
    1013            this = self.sql(expression, "this")
    1014
    1015        return f"COMPRESS {this}"
    1016
    1017    def generatedasidentitycolumnconstraint_sql(
    1018        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1019    ) -> str:
    1020        this = ""
    1021        if expression.this is not None:
    1022            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1023            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1024
    1025        start = expression.args.get("start")
    1026        start = f"START WITH {start}" if start else ""
    1027        increment = expression.args.get("increment")
    1028        increment = f" INCREMENT BY {increment}" if increment else ""
    1029        minvalue = expression.args.get("minvalue")
    1030        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1031        maxvalue = expression.args.get("maxvalue")
    1032        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1033        cycle = expression.args.get("cycle")
    1034        cycle_sql = ""
    1035
    1036        if cycle is not None:
    1037            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1038            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1039
    1040        sequence_opts = ""
    1041        if start or increment or cycle_sql:
    1042            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1043            sequence_opts = f" ({sequence_opts.strip()})"
    1044
    1045        expr = self.sql(expression, "expression")
    1046        expr = f"({expr})" if expr else "IDENTITY"
    1047
    1048        return f"GENERATED{this} AS {expr}{sequence_opts}"
    1049
    1050    def generatedasrowcolumnconstraint_sql(
    1051        self, expression: exp.GeneratedAsRowColumnConstraint
    1052    ) -> str:
    1053        start = "START" if expression.args.get("start") else "END"
    1054        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1055        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
    1056
    1057    def periodforsystemtimeconstraint_sql(
    1058        self, expression: exp.PeriodForSystemTimeConstraint
    1059    ) -> str:
    1060        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
    1061
    1062    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
    1063        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
    1064
    1065    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
    1066        return f"AS {self.sql(expression, 'this')}"
    1067
    1068    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1069        desc = expression.args.get("desc")
    1070        if desc is not None:
    1071            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1072        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1073        options = f" {options}" if options else ""
    1074        return f"PRIMARY KEY{options}"
    1075
    1076    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1077        this = self.sql(expression, "this")
    1078        this = f" {this}" if this else ""
    1079        index_type = expression.args.get("index_type")
    1080        index_type = f" USING {index_type}" if index_type else ""
    1081        on_conflict = self.sql(expression, "on_conflict")
    1082        on_conflict = f" {on_conflict}" if on_conflict else ""
    1083        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1084        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1085        options = f" {options}" if options else ""
    1086        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
    1087
    1088    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
    1089        return self.sql(expression, "this")
    1090
    1091    def create_sql(self, expression: exp.Create) -> str:
    1092        kind = self.sql(expression, "kind")
    1093        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1094        properties = expression.args.get("properties")
    1095        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1096
    1097        this = self.createable_sql(expression, properties_locs)
    1098
    1099        properties_sql = ""
    1100        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1101            exp.Properties.Location.POST_WITH
    1102        ):
    1103            properties_sql = self.sql(
    1104                exp.Properties(
    1105                    expressions=[
    1106                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1107                        *properties_locs[exp.Properties.Location.POST_WITH],
    1108                    ]
    1109                )
    1110            )
    1111
    1112            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1113                properties_sql = self.sep() + properties_sql
    1114            elif not self.pretty:
    1115                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1116                properties_sql = f" {properties_sql}"
    1117
    1118        begin = " BEGIN" if expression.args.get("begin") else ""
    1119        end = " END" if expression.args.get("end") else ""
    1120
    1121        expression_sql = self.sql(expression, "expression")
    1122        if expression_sql:
    1123            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1124
    1125            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1126                postalias_props_sql = ""
    1127                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1128                    postalias_props_sql = self.properties(
    1129                        exp.Properties(
    1130                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1131                        ),
    1132                        wrapped=False,
    1133                    )
    1134                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1135                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1136
    1137        postindex_props_sql = ""
    1138        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1139            postindex_props_sql = self.properties(
    1140                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1141                wrapped=False,
    1142                prefix=" ",
    1143            )
    1144
    1145        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1146        indexes = f" {indexes}" if indexes else ""
    1147        index_sql = indexes + postindex_props_sql
    1148
    1149        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1150        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1151        unique = " UNIQUE" if expression.args.get("unique") else ""
    1152
    1153        clustered = expression.args.get("clustered")
    1154        if clustered is None:
    1155            clustered_sql = ""
    1156        elif clustered:
    1157            clustered_sql = " CLUSTERED COLUMNSTORE"
    1158        else:
    1159            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1160
    1161        postcreate_props_sql = ""
    1162        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1163            postcreate_props_sql = self.properties(
    1164                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1165                sep=" ",
    1166                prefix=" ",
    1167                wrapped=False,
    1168            )
    1169
    1170        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1171
    1172        postexpression_props_sql = ""
    1173        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1174            postexpression_props_sql = self.properties(
    1175                exp.Properties(
    1176                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1177                ),
    1178                sep=" ",
    1179                prefix=" ",
    1180                wrapped=False,
    1181            )
    1182
    1183        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1184        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1185        no_schema_binding = (
    1186            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1187        )
    1188
    1189        clone = self.sql(expression, "clone")
    1190        clone = f" {clone}" if clone else ""
    1191
    1192        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1193            properties_expression = f"{expression_sql}{properties_sql}"
    1194        else:
    1195            properties_expression = f"{properties_sql}{expression_sql}"
    1196
    1197        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1198        return self.prepend_ctes(expression, expression_sql)
    1199
    1200    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1201        start = self.sql(expression, "start")
    1202        start = f"START WITH {start}" if start else ""
    1203        increment = self.sql(expression, "increment")
    1204        increment = f" INCREMENT BY {increment}" if increment else ""
    1205        minvalue = self.sql(expression, "minvalue")
    1206        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1207        maxvalue = self.sql(expression, "maxvalue")
    1208        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1209        owned = self.sql(expression, "owned")
    1210        owned = f" OWNED BY {owned}" if owned else ""
    1211
    1212        cache = expression.args.get("cache")
    1213        if cache is None:
    1214            cache_str = ""
    1215        elif cache is True:
    1216            cache_str = " CACHE"
    1217        else:
    1218            cache_str = f" CACHE {cache}"
    1219
    1220        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1221        options = f" {options}" if options else ""
    1222
    1223        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
    1224
    1225    def clone_sql(self, expression: exp.Clone) -> str:
    1226        this = self.sql(expression, "this")
    1227        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1228        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1229        return f"{shallow}{keyword} {this}"
    1230
    1231    def describe_sql(self, expression: exp.Describe) -> str:
    1232        style = expression.args.get("style")
    1233        style = f" {style}" if style else ""
    1234        partition = self.sql(expression, "partition")
    1235        partition = f" {partition}" if partition else ""
    1236        format = self.sql(expression, "format")
    1237        format = f" {format}" if format else ""
    1238
    1239        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
    1240
    1241    def heredoc_sql(self, expression: exp.Heredoc) -> str:
    1242        tag = self.sql(expression, "tag")
    1243        return f"${tag}${self.sql(expression, 'this')}${tag}$"
    1244
    1245    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1246        with_ = self.sql(expression, "with")
    1247        if with_:
    1248            sql = f"{with_}{self.sep()}{sql}"
    1249        return sql
    1250
    1251    def with_sql(self, expression: exp.With) -> str:
    1252        sql = self.expressions(expression, flat=True)
    1253        recursive = (
    1254            "RECURSIVE "
    1255            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1256            else ""
    1257        )
    1258        search = self.sql(expression, "search")
    1259        search = f" {search}" if search else ""
    1260
    1261        return f"WITH {recursive}{sql}{search}"
    1262
    1263    def cte_sql(self, expression: exp.CTE) -> str:
    1264        alias = expression.args.get("alias")
    1265        if alias:
    1266            alias.add_comments(expression.pop_comments())
    1267
    1268        alias_sql = self.sql(expression, "alias")
    1269
    1270        materialized = expression.args.get("materialized")
    1271        if materialized is False:
    1272            materialized = "NOT MATERIALIZED "
    1273        elif materialized:
    1274            materialized = "MATERIALIZED "
    1275
    1276        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
    1277
    1278    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1279        alias = self.sql(expression, "this")
    1280        columns = self.expressions(expression, key="columns", flat=True)
    1281        columns = f"({columns})" if columns else ""
    1282
    1283        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1284            columns = ""
    1285            self.unsupported("Named columns are not supported in table alias.")
    1286
    1287        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1288            alias = self._next_name()
    1289
    1290        return f"{alias}{columns}"
    1291
    1292    def bitstring_sql(self, expression: exp.BitString) -> str:
    1293        this = self.sql(expression, "this")
    1294        if self.dialect.BIT_START:
    1295            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1296        return f"{int(this, 2)}"
    1297
    1298    def hexstring_sql(
    1299        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
    1300    ) -> str:
    1301        this = self.sql(expression, "this")
    1302        is_integer_type = expression.args.get("is_integer")
    1303
    1304        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
    1305            not self.dialect.HEX_START and not binary_function_repr
    1306        ):
    1307            # Integer representation will be returned if:
    1308            # - The read dialect treats the hex value as integer literal but not the write
    1309            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
    1310            return f"{int(this, 16)}"
    1311
    1312        if not is_integer_type:
    1313            # Read dialect treats the hex value as BINARY/BLOB
    1314            if binary_function_repr:
    1315                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
    1316                return self.func(binary_function_repr, exp.Literal.string(this))
    1317            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
    1318                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
    1319                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
    1320
    1321        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1322
    1323    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1324        this = self.sql(expression, "this")
    1325        if self.dialect.BYTE_START:
    1326            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1327        return this
    1328
    1329    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1330        this = self.sql(expression, "this")
    1331        escape = expression.args.get("escape")
    1332
    1333        if self.dialect.UNICODE_START:
    1334            escape_substitute = r"\\\1"
    1335            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1336        else:
    1337            escape_substitute = r"\\u\1"
    1338            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1339
    1340        if escape:
    1341            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1342            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1343        else:
    1344            escape_pattern = ESCAPED_UNICODE_RE
    1345            escape_sql = ""
    1346
    1347        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1348            this = escape_pattern.sub(escape_substitute, this)
    1349
    1350        return f"{left_quote}{this}{right_quote}{escape_sql}"
    1351
    1352    def rawstring_sql(self, expression: exp.RawString) -> str:
    1353        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
    1354        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
    1355
    1356    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1357        this = self.sql(expression, "this")
    1358        specifier = self.sql(expression, "expression")
    1359        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1360        return f"{this}{specifier}"
    1361
    1362    def datatype_sql(self, expression: exp.DataType) -> str:
    1363        nested = ""
    1364        values = ""
    1365        interior = self.expressions(expression, flat=True)
    1366
    1367        type_value = expression.this
    1368        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1369            type_sql = self.sql(expression, "kind")
    1370        else:
    1371            type_sql = (
    1372                self.TYPE_MAPPING.get(type_value, type_value.value)
    1373                if isinstance(type_value, exp.DataType.Type)
    1374                else type_value
    1375            )
    1376
    1377        if interior:
    1378            if expression.args.get("nested"):
    1379                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1380                if expression.args.get("values") is not None:
    1381                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1382                    values = self.expressions(expression, key="values", flat=True)
    1383                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1384            elif type_value == exp.DataType.Type.INTERVAL:
    1385                nested = f" {interior}"
    1386            else:
    1387                nested = f"({interior})"
    1388
    1389        type_sql = f"{type_sql}{nested}{values}"
    1390        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1391            exp.DataType.Type.TIMETZ,
    1392            exp.DataType.Type.TIMESTAMPTZ,
    1393        ):
    1394            type_sql = f"{type_sql} WITH TIME ZONE"
    1395
    1396        return type_sql
    1397
    1398    def directory_sql(self, expression: exp.Directory) -> str:
    1399        local = "LOCAL " if expression.args.get("local") else ""
    1400        row_format = self.sql(expression, "row_format")
    1401        row_format = f" {row_format}" if row_format else ""
    1402        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
    1403
    1404    def delete_sql(self, expression: exp.Delete) -> str:
    1405        this = self.sql(expression, "this")
    1406        this = f" FROM {this}" if this else ""
    1407        using = self.sql(expression, "using")
    1408        using = f" USING {using}" if using else ""
    1409        cluster = self.sql(expression, "cluster")
    1410        cluster = f" {cluster}" if cluster else ""
    1411        where = self.sql(expression, "where")
    1412        returning = self.sql(expression, "returning")
    1413        limit = self.sql(expression, "limit")
    1414        tables = self.expressions(expression, key="tables")
    1415        tables = f" {tables}" if tables else ""
    1416        if self.RETURNING_END:
    1417            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1418        else:
    1419            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1420        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
    1421
    1422    def drop_sql(self, expression: exp.Drop) -> str:
    1423        this = self.sql(expression, "this")
    1424        expressions = self.expressions(expression, flat=True)
    1425        expressions = f" ({expressions})" if expressions else ""
    1426        kind = expression.args["kind"]
    1427        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1428        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1429        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1430        on_cluster = self.sql(expression, "cluster")
    1431        on_cluster = f" {on_cluster}" if on_cluster else ""
    1432        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1433        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1434        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1435        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1436        purge = " PURGE" if expression.args.get("purge") else ""
    1437        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
    1438
    1439    def set_operation(self, expression: exp.SetOperation) -> str:
    1440        op_type = type(expression)
    1441        op_name = op_type.key.upper()
    1442
    1443        distinct = expression.args.get("distinct")
    1444        if (
    1445            distinct is False
    1446            and op_type in (exp.Except, exp.Intersect)
    1447            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1448        ):
    1449            self.unsupported(f"{op_name} ALL is not supported")
    1450
    1451        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1452
    1453        if distinct is None:
    1454            distinct = default_distinct
    1455            if distinct is None:
    1456                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1457
    1458        if distinct is default_distinct:
    1459            distinct_or_all = ""
    1460        else:
    1461            distinct_or_all = " DISTINCT" if distinct else " ALL"
    1462
    1463        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
    1464        side_kind = f"{side_kind} " if side_kind else ""
    1465
    1466        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1467        on = self.expressions(expression, key="on", flat=True)
    1468        on = f" ON ({on})" if on else ""
    1469
    1470        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
    1471
    1472    def set_operations(self, expression: exp.SetOperation) -> str:
    1473        if not self.SET_OP_MODIFIERS:
    1474            limit = expression.args.get("limit")
    1475            order = expression.args.get("order")
    1476
    1477            if limit or order:
    1478                select = self._move_ctes_to_top_level(
    1479                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1480                )
    1481
    1482                if limit:
    1483                    select = select.limit(limit.pop(), copy=False)
    1484                if order:
    1485                    select = select.order_by(order.pop(), copy=False)
    1486                return self.sql(select)
    1487
    1488        sqls: t.List[str] = []
    1489        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1490
    1491        while stack:
    1492            node = stack.pop()
    1493
    1494            if isinstance(node, exp.SetOperation):
    1495                stack.append(node.expression)
    1496                stack.append(
    1497                    self.maybe_comment(
    1498                        self.set_operation(node), comments=node.comments, separated=True
    1499                    )
    1500                )
    1501                stack.append(node.this)
    1502            else:
    1503                sqls.append(self.sql(node))
    1504
    1505        this = self.sep().join(sqls)
    1506        this = self.query_modifiers(expression, this)
    1507        return self.prepend_ctes(expression, this)
    1508
    1509    def fetch_sql(self, expression: exp.Fetch) -> str:
    1510        direction = expression.args.get("direction")
    1511        direction = f" {direction}" if direction else ""
    1512        count = self.sql(expression, "count")
    1513        count = f" {count}" if count else ""
    1514        limit_options = self.sql(expression, "limit_options")
    1515        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
    1516        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
    1517
    1518    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
    1519        percent = " PERCENT" if expression.args.get("percent") else ""
    1520        rows = " ROWS" if expression.args.get("rows") else ""
    1521        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
    1522        if not with_ties and rows:
    1523            with_ties = " ONLY"
    1524        return f"{percent}{rows}{with_ties}"
    1525
    1526    def filter_sql(self, expression: exp.Filter) -> str:
    1527        if self.AGGREGATE_FILTER_SUPPORTED:
    1528            this = self.sql(expression, "this")
    1529            where = self.sql(expression, "expression").strip()
    1530            return f"{this} FILTER({where})"
    1531
    1532        agg = expression.this
    1533        agg_arg = agg.this
    1534        cond = expression.expression.this
    1535        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1536        return self.sql(agg)
    1537
    1538    def hint_sql(self, expression: exp.Hint) -> str:
    1539        if not self.QUERY_HINTS:
    1540            self.unsupported("Hints are not supported")
    1541            return ""
    1542
    1543        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
    1544
    1545    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1546        using = self.sql(expression, "using")
    1547        using = f" USING {using}" if using else ""
    1548        columns = self.expressions(expression, key="columns", flat=True)
    1549        columns = f"({columns})" if columns else ""
    1550        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1551        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1552        where = self.sql(expression, "where")
    1553        include = self.expressions(expression, key="include", flat=True)
    1554        if include:
    1555            include = f" INCLUDE ({include})"
    1556        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1557        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1558        tablespace = self.sql(expression, "tablespace")
    1559        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1560        on = self.sql(expression, "on")
    1561        on = f" ON {on}" if on else ""
    1562
    1563        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
    1564
    1565    def index_sql(self, expression: exp.Index) -> str:
    1566        unique = "UNIQUE " if expression.args.get("unique") else ""
    1567        primary = "PRIMARY " if expression.args.get("primary") else ""
    1568        amp = "AMP " if expression.args.get("amp") else ""
    1569        name = self.sql(expression, "this")
    1570        name = f"{name} " if name else ""
    1571        table = self.sql(expression, "table")
    1572        table = f"{self.INDEX_ON} {table}" if table else ""
    1573
    1574        index = "INDEX " if not table else ""
    1575
    1576        params = self.sql(expression, "params")
    1577        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
    1578
    1579    def identifier_sql(self, expression: exp.Identifier) -> str:
    1580        text = expression.name
    1581        lower = text.lower()
    1582        text = lower if self.normalize and not expression.quoted else text
    1583        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1584        if (
    1585            expression.quoted
    1586            or self.dialect.can_identify(text, self.identify)
    1587            or lower in self.RESERVED_KEYWORDS
    1588            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1589        ):
    1590            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1591        return text
    1592
    1593    def hex_sql(self, expression: exp.Hex) -> str:
    1594        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1595        if self.dialect.HEX_LOWERCASE:
    1596            text = self.func("LOWER", text)
    1597
    1598        return text
    1599
    1600    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1601        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1602        if not self.dialect.HEX_LOWERCASE:
    1603            text = self.func("LOWER", text)
    1604        return text
    1605
    1606    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1607        input_format = self.sql(expression, "input_format")
    1608        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1609        output_format = self.sql(expression, "output_format")
    1610        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1611        return self.sep().join((input_format, output_format))
    1612
    1613    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1614        string = self.sql(exp.Literal.string(expression.name))
    1615        return f"{prefix}{string}"
    1616
    1617    def partition_sql(self, expression: exp.Partition) -> str:
    1618        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
    1619        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
    1620
    1621    def properties_sql(self, expression: exp.Properties) -> str:
    1622        root_properties = []
    1623        with_properties = []
    1624
    1625        for p in expression.expressions:
    1626            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1627            if p_loc == exp.Properties.Location.POST_WITH:
    1628                with_properties.append(p)
    1629            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1630                root_properties.append(p)
    1631
    1632        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1633        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1634
    1635        if root_props and with_props and not self.pretty:
    1636            with_props = " " + with_props
    1637
    1638        return root_props + with_props
    1639
    1640    def root_properties(self, properties: exp.Properties) -> str:
    1641        if properties.expressions:
    1642            return self.expressions(properties, indent=False, sep=" ")
    1643        return ""
    1644
    1645    def properties(
    1646        self,
    1647        properties: exp.Properties,
    1648        prefix: str = "",
    1649        sep: str = ", ",
    1650        suffix: str = "",
    1651        wrapped: bool = True,
    1652    ) -> str:
    1653        if properties.expressions:
    1654            expressions = self.expressions(properties, sep=sep, indent=False)
    1655            if expressions:
    1656                expressions = self.wrap(expressions) if wrapped else expressions
    1657                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1658        return ""
    1659
    1660    def with_properties(self, properties: exp.Properties) -> str:
    1661        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
    1662
    1663    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1664        properties_locs = defaultdict(list)
    1665        for p in properties.expressions:
    1666            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1667            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1668                properties_locs[p_loc].append(p)
    1669            else:
    1670                self.unsupported(f"Unsupported property {p.key}")
    1671
    1672        return properties_locs
    1673
    1674    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1675        if isinstance(expression.this, exp.Dot):
    1676            return self.sql(expression, "this")
    1677        return f"'{expression.name}'" if string_key else expression.name
    1678
    1679    def property_sql(self, expression: exp.Property) -> str:
    1680        property_cls = expression.__class__
    1681        if property_cls == exp.Property:
    1682            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1683
    1684        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1685        if not property_name:
    1686            self.unsupported(f"Unsupported property {expression.key}")
    1687
    1688        return f"{property_name}={self.sql(expression, 'this')}"
    1689
    1690    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1691        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1692            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1693            options = f" {options}" if options else ""
    1694
    1695            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1696            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1697                like = f"({like})"
    1698
    1699            return like
    1700
    1701        if expression.expressions:
    1702            self.unsupported("Transpilation of LIKE property options is unsupported")
    1703
    1704        select = exp.select("*").from_(expression.this).limit(0)
    1705        return f"AS {self.sql(select)}"
    1706
    1707    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1708        no = "NO " if expression.args.get("no") else ""
    1709        protection = " PROTECTION" if expression.args.get("protection") else ""
    1710        return f"{no}FALLBACK{protection}"
    1711
    1712    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1713        no = "NO " if expression.args.get("no") else ""
    1714        local = expression.args.get("local")
    1715        local = f"{local} " if local else ""
    1716        dual = "DUAL " if expression.args.get("dual") else ""
    1717        before = "BEFORE " if expression.args.get("before") else ""
    1718        after = "AFTER " if expression.args.get("after") else ""
    1719        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1720
    1721    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1722        freespace = self.sql(expression, "this")
    1723        percent = " PERCENT" if expression.args.get("percent") else ""
    1724        return f"FREESPACE={freespace}{percent}"
    1725
    1726    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1727        if expression.args.get("default"):
    1728            property = "DEFAULT"
    1729        elif expression.args.get("on"):
    1730            property = "ON"
    1731        else:
    1732            property = "OFF"
    1733        return f"CHECKSUM={property}"
    1734
    1735    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1736        if expression.args.get("no"):
    1737            return "NO MERGEBLOCKRATIO"
    1738        if expression.args.get("default"):
    1739            return "DEFAULT MERGEBLOCKRATIO"
    1740
    1741        percent = " PERCENT" if expression.args.get("percent") else ""
    1742        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1743
    1744    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1745        default = expression.args.get("default")
    1746        minimum = expression.args.get("minimum")
    1747        maximum = expression.args.get("maximum")
    1748        if default or minimum or maximum:
    1749            if default:
    1750                prop = "DEFAULT"
    1751            elif minimum:
    1752                prop = "MINIMUM"
    1753            else:
    1754                prop = "MAXIMUM"
    1755            return f"{prop} DATABLOCKSIZE"
    1756        units = expression.args.get("units")
    1757        units = f" {units}" if units else ""
    1758        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1759
    1760    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1761        autotemp = expression.args.get("autotemp")
    1762        always = expression.args.get("always")
    1763        default = expression.args.get("default")
    1764        manual = expression.args.get("manual")
    1765        never = expression.args.get("never")
    1766
    1767        if autotemp is not None:
    1768            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1769        elif always:
    1770            prop = "ALWAYS"
    1771        elif default:
    1772            prop = "DEFAULT"
    1773        elif manual:
    1774            prop = "MANUAL"
    1775        elif never:
    1776            prop = "NEVER"
    1777        return f"BLOCKCOMPRESSION={prop}"
    1778
    1779    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1780        no = expression.args.get("no")
    1781        no = " NO" if no else ""
    1782        concurrent = expression.args.get("concurrent")
    1783        concurrent = " CONCURRENT" if concurrent else ""
    1784        target = self.sql(expression, "target")
    1785        target = f" {target}" if target else ""
    1786        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
    1787
    1788    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1789        if isinstance(expression.this, list):
    1790            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1791        if expression.this:
    1792            modulus = self.sql(expression, "this")
    1793            remainder = self.sql(expression, "expression")
    1794            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1795
    1796        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1797        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1798        return f"FROM ({from_expressions}) TO ({to_expressions})"
    1799
    1800    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1801        this = self.sql(expression, "this")
    1802
    1803        for_values_or_default = expression.expression
    1804        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1805            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1806        else:
    1807            for_values_or_default = " DEFAULT"
    1808
    1809        return f"PARTITION OF {this}{for_values_or_default}"
    1810
    1811    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1812        kind = expression.args.get("kind")
    1813        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1814        for_or_in = expression.args.get("for_or_in")
    1815        for_or_in = f" {for_or_in}" if for_or_in else ""
    1816        lock_type = expression.args.get("lock_type")
    1817        override = " OVERRIDE" if expression.args.get("override") else ""
    1818        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
    1819
    1820    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1821        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1822        statistics = expression.args.get("statistics")
    1823        statistics_sql = ""
    1824        if statistics is not None:
    1825            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1826        return f"{data_sql}{statistics_sql}"
    1827
    1828    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1829        this = self.sql(expression, "this")
    1830        this = f"HISTORY_TABLE={this}" if this else ""
    1831        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1832        data_consistency = (
    1833            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1834        )
    1835        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1836        retention_period = (
    1837            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1838        )
    1839
    1840        if this:
    1841            on_sql = self.func("ON", this, data_consistency, retention_period)
    1842        else:
    1843            on_sql = "ON" if expression.args.get("on") else "OFF"
    1844
    1845        sql = f"SYSTEM_VERSIONING={on_sql}"
    1846
    1847        return f"WITH({sql})" if expression.args.get("with") else sql
    1848
    1849    def insert_sql(self, expression: exp.Insert) -> str:
    1850        hint = self.sql(expression, "hint")
    1851        overwrite = expression.args.get("overwrite")
    1852
    1853        if isinstance(expression.this, exp.Directory):
    1854            this = " OVERWRITE" if overwrite else " INTO"
    1855        else:
    1856            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1857
    1858        stored = self.sql(expression, "stored")
    1859        stored = f" {stored}" if stored else ""
    1860        alternative = expression.args.get("alternative")
    1861        alternative = f" OR {alternative}" if alternative else ""
    1862        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1863        is_function = expression.args.get("is_function")
    1864        if is_function:
    1865            this = f"{this} FUNCTION"
    1866        this = f"{this} {self.sql(expression, 'this')}"
    1867
    1868        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1869        where = self.sql(expression, "where")
    1870        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1871        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1872        on_conflict = self.sql(expression, "conflict")
    1873        on_conflict = f" {on_conflict}" if on_conflict else ""
    1874        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1875        returning = self.sql(expression, "returning")
    1876
    1877        if self.RETURNING_END:
    1878            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1879        else:
    1880            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1881
    1882        partition_by = self.sql(expression, "partition")
    1883        partition_by = f" {partition_by}" if partition_by else ""
    1884        settings = self.sql(expression, "settings")
    1885        settings = f" {settings}" if settings else ""
    1886
    1887        source = self.sql(expression, "source")
    1888        source = f"TABLE {source}" if source else ""
    1889
    1890        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1891        return self.prepend_ctes(expression, sql)
    1892
    1893    def introducer_sql(self, expression: exp.Introducer) -> str:
    1894        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1895
    1896    def kill_sql(self, expression: exp.Kill) -> str:
    1897        kind = self.sql(expression, "kind")
    1898        kind = f" {kind}" if kind else ""
    1899        this = self.sql(expression, "this")
    1900        this = f" {this}" if this else ""
    1901        return f"KILL{kind}{this}"
    1902
    1903    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1904        return expression.name
    1905
    1906    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1907        return expression.name
    1908
    1909    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1910        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1911
    1912        constraint = self.sql(expression, "constraint")
    1913        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1914
    1915        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1916        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1917        action = self.sql(expression, "action")
    1918
    1919        expressions = self.expressions(expression, flat=True)
    1920        if expressions:
    1921            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1922            expressions = f" {set_keyword}{expressions}"
    1923
    1924        where = self.sql(expression, "where")
    1925        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
    1926
    1927    def returning_sql(self, expression: exp.Returning) -> str:
    1928        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1929
    1930    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1931        fields = self.sql(expression, "fields")
    1932        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1933        escaped = self.sql(expression, "escaped")
    1934        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1935        items = self.sql(expression, "collection_items")
    1936        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1937        keys = self.sql(expression, "map_keys")
    1938        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1939        lines = self.sql(expression, "lines")
    1940        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1941        null = self.sql(expression, "null")
    1942        null = f" NULL DEFINED AS {null}" if null else ""
    1943        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1944
    1945    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1946        return f"WITH ({self.expressions(expression, flat=True)})"
    1947
    1948    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1949        this = f"{self.sql(expression, 'this')} INDEX"
    1950        target = self.sql(expression, "target")
    1951        target = f" FOR {target}" if target else ""
    1952        return f"{this}{target} ({self.expressions(expression, flat=True)})"
    1953
    1954    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1955        this = self.sql(expression, "this")
    1956        kind = self.sql(expression, "kind")
    1957        expr = self.sql(expression, "expression")
    1958        return f"{this} ({kind} => {expr})"
    1959
    1960    def table_parts(self, expression: exp.Table) -> str:
    1961        return ".".join(
    1962            self.sql(part)
    1963            for part in (
    1964                expression.args.get("catalog"),
    1965                expression.args.get("db"),
    1966                expression.args.get("this"),
    1967            )
    1968            if part is not None
    1969        )
    1970
    1971    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1972        table = self.table_parts(expression)
    1973        only = "ONLY " if expression.args.get("only") else ""
    1974        partition = self.sql(expression, "partition")
    1975        partition = f" {partition}" if partition else ""
    1976        version = self.sql(expression, "version")
    1977        version = f" {version}" if version else ""
    1978        alias = self.sql(expression, "alias")
    1979        alias = f"{sep}{alias}" if alias else ""
    1980
    1981        sample = self.sql(expression, "sample")
    1982        if self.dialect.ALIAS_POST_TABLESAMPLE:
    1983            sample_pre_alias = sample
    1984            sample_post_alias = ""
    1985        else:
    1986            sample_pre_alias = ""
    1987            sample_post_alias = sample
    1988
    1989        hints = self.expressions(expression, key="hints", sep=" ")
    1990        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1991        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    1992        joins = self.indent(
    1993            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1994        )
    1995        laterals = self.expressions(expression, key="laterals", sep="")
    1996
    1997        file_format = self.sql(expression, "format")
    1998        if file_format:
    1999            pattern = self.sql(expression, "pattern")
    2000            pattern = f", PATTERN => {pattern}" if pattern else ""
    2001            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    2002
    2003        ordinality = expression.args.get("ordinality") or ""
    2004        if ordinality:
    2005            ordinality = f" WITH ORDINALITY{alias}"
    2006            alias = ""
    2007
    2008        when = self.sql(expression, "when")
    2009        if when:
    2010            table = f"{table} {when}"
    2011
    2012        changes = self.sql(expression, "changes")
    2013        changes = f" {changes}" if changes else ""
    2014
    2015        rows_from = self.expressions(expression, key="rows_from")
    2016        if rows_from:
    2017            table = f"ROWS FROM {self.wrap(rows_from)}"
    2018
    2019        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
    2020
    2021    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
    2022        table = self.func("TABLE", expression.this)
    2023        alias = self.sql(expression, "alias")
    2024        alias = f" AS {alias}" if alias else ""
    2025        sample = self.sql(expression, "sample")
    2026        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2027        joins = self.indent(
    2028            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    2029        )
    2030        return f"{table}{alias}{pivots}{sample}{joins}"
    2031
    2032    def tablesample_sql(
    2033        self,
    2034        expression: exp.TableSample,
    2035        tablesample_keyword: t.Optional[str] = None,
    2036    ) -> str:
    2037        method = self.sql(expression, "method")
    2038        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    2039        numerator = self.sql(expression, "bucket_numerator")
    2040        denominator = self.sql(expression, "bucket_denominator")
    2041        field = self.sql(expression, "bucket_field")
    2042        field = f" ON {field}" if field else ""
    2043        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    2044        seed = self.sql(expression, "seed")
    2045        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    2046
    2047        size = self.sql(expression, "size")
    2048        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    2049            size = f"{size} ROWS"
    2050
    2051        percent = self.sql(expression, "percent")
    2052        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    2053            percent = f"{percent} PERCENT"
    2054
    2055        expr = f"{bucket}{percent}{size}"
    2056        if self.TABLESAMPLE_REQUIRES_PARENS:
    2057            expr = f"({expr})"
    2058
    2059        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
    2060
    2061    def pivot_sql(self, expression: exp.Pivot) -> str:
    2062        expressions = self.expressions(expression, flat=True)
    2063        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2064
    2065        group = self.sql(expression, "group")
    2066
    2067        if expression.this:
    2068            this = self.sql(expression, "this")
    2069            if not expressions:
    2070                return f"UNPIVOT {this}"
    2071
    2072            on = f"{self.seg('ON')} {expressions}"
    2073            into = self.sql(expression, "into")
    2074            into = f"{self.seg('INTO')} {into}" if into else ""
    2075            using = self.expressions(expression, key="using", flat=True)
    2076            using = f"{self.seg('USING')} {using}" if using else ""
    2077            return f"{direction} {this}{on}{into}{using}{group}"
    2078
    2079        alias = self.sql(expression, "alias")
    2080        alias = f" AS {alias}" if alias else ""
    2081
    2082        fields = self.expressions(
    2083            expression,
    2084            "fields",
    2085            sep=" ",
    2086            dynamic=True,
    2087            new_line=True,
    2088            skip_first=True,
    2089            skip_last=True,
    2090        )
    2091
    2092        include_nulls = expression.args.get("include_nulls")
    2093        if include_nulls is not None:
    2094            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2095        else:
    2096            nulls = ""
    2097
    2098        default_on_null = self.sql(expression, "default_on_null")
    2099        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2100        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
    2101
    2102    def version_sql(self, expression: exp.Version) -> str:
    2103        this = f"FOR {expression.name}"
    2104        kind = expression.text("kind")
    2105        expr = self.sql(expression, "expression")
    2106        return f"{this} {kind} {expr}"
    2107
    2108    def tuple_sql(self, expression: exp.Tuple) -> str:
    2109        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    2110
    2111    def update_sql(self, expression: exp.Update) -> str:
    2112        this = self.sql(expression, "this")
    2113        set_sql = self.expressions(expression, flat=True)
    2114        from_sql = self.sql(expression, "from")
    2115        where_sql = self.sql(expression, "where")
    2116        returning = self.sql(expression, "returning")
    2117        order = self.sql(expression, "order")
    2118        limit = self.sql(expression, "limit")
    2119        if self.RETURNING_END:
    2120            expression_sql = f"{from_sql}{where_sql}{returning}"
    2121        else:
    2122            expression_sql = f"{returning}{from_sql}{where_sql}"
    2123        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2124        return self.prepend_ctes(expression, sql)
    2125
    2126    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2127        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2128
    2129        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2130        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2131            args = self.expressions(expression)
    2132            alias = self.sql(expression, "alias")
    2133            values = f"VALUES{self.seg('')}{args}"
    2134            values = (
    2135                f"({values})"
    2136                if self.WRAP_DERIVED_VALUES
    2137                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2138                else values
    2139            )
    2140            return f"{values} AS {alias}" if alias else values
    2141
    2142        # Converts `VALUES...` expression into a series of select unions.
    2143        alias_node = expression.args.get("alias")
    2144        column_names = alias_node and alias_node.columns
    2145
    2146        selects: t.List[exp.Query] = []
    2147
    2148        for i, tup in enumerate(expression.expressions):
    2149            row = tup.expressions
    2150
    2151            if i == 0 and column_names:
    2152                row = [
    2153                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2154                ]
    2155
    2156            selects.append(exp.Select(expressions=row))
    2157
    2158        if self.pretty:
    2159            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2160            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2161            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2162            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2163            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2164
    2165        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2166        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2167        return f"({unions}){alias}"
    2168
    2169    def var_sql(self, expression: exp.Var) -> str:
    2170        return self.sql(expression, "this")
    2171
    2172    @unsupported_args("expressions")
    2173    def into_sql(self, expression: exp.Into) -> str:
    2174        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2175        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2176        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    2177
    2178    def from_sql(self, expression: exp.From) -> str:
    2179        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    2180
    2181    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
    2182        grouping_sets = self.expressions(expression, indent=False)
    2183        return f"GROUPING SETS {self.wrap(grouping_sets)}"
    2184
    2185    def rollup_sql(self, expression: exp.Rollup) -> str:
    2186        expressions = self.expressions(expression, indent=False)
    2187        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
    2188
    2189    def cube_sql(self, expression: exp.Cube) -> str:
    2190        expressions = self.expressions(expression, indent=False)
    2191        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
    2192
    2193    def group_sql(self, expression: exp.Group) -> str:
    2194        group_by_all = expression.args.get("all")
    2195        if group_by_all is True:
    2196            modifier = " ALL"
    2197        elif group_by_all is False:
    2198            modifier = " DISTINCT"
    2199        else:
    2200            modifier = ""
    2201
    2202        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2203
    2204        grouping_sets = self.expressions(expression, key="grouping_sets")
    2205        cube = self.expressions(expression, key="cube")
    2206        rollup = self.expressions(expression, key="rollup")
    2207
    2208        groupings = csv(
    2209            self.seg(grouping_sets) if grouping_sets else "",
    2210            self.seg(cube) if cube else "",
    2211            self.seg(rollup) if rollup else "",
    2212            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2213            sep=self.GROUPINGS_SEP,
    2214        )
    2215
    2216        if (
    2217            expression.expressions
    2218            and groupings
    2219            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2220        ):
    2221            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2222
    2223        return f"{group_by}{groupings}"
    2224
    2225    def having_sql(self, expression: exp.Having) -> str:
    2226        this = self.indent(self.sql(expression, "this"))
    2227        return f"{self.seg('HAVING')}{self.sep()}{this}"
    2228
    2229    def connect_sql(self, expression: exp.Connect) -> str:
    2230        start = self.sql(expression, "start")
    2231        start = self.seg(f"START WITH {start}") if start else ""
    2232        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2233        connect = self.sql(expression, "connect")
    2234        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2235        return start + connect
    2236
    2237    def prior_sql(self, expression: exp.Prior) -> str:
    2238        return f"PRIOR {self.sql(expression, 'this')}"
    2239
    2240    def join_sql(self, expression: exp.Join) -> str:
    2241        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2242            side = None
    2243        else:
    2244            side = expression.side
    2245
    2246        op_sql = " ".join(
    2247            op
    2248            for op in (
    2249                expression.method,
    2250                "GLOBAL" if expression.args.get("global") else None,
    2251                side,
    2252                expression.kind,
    2253                expression.hint if self.JOIN_HINTS else None,
    2254            )
    2255            if op
    2256        )
    2257        match_cond = self.sql(expression, "match_condition")
    2258        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2259        on_sql = self.sql(expression, "on")
    2260        using = expression.args.get("using")
    2261
    2262        if not on_sql and using:
    2263            on_sql = csv(*(self.sql(column) for column in using))
    2264
    2265        this = expression.this
    2266        this_sql = self.sql(this)
    2267
    2268        exprs = self.expressions(expression)
    2269        if exprs:
    2270            this_sql = f"{this_sql},{self.seg(exprs)}"
    2271
    2272        if on_sql:
    2273            on_sql = self.indent(on_sql, skip_first=True)
    2274            space = self.seg(" " * self.pad) if self.pretty else " "
    2275            if using:
    2276                on_sql = f"{space}USING ({on_sql})"
    2277            else:
    2278                on_sql = f"{space}ON {on_sql}"
    2279        elif not op_sql:
    2280            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2281                return f" {this_sql}"
    2282
    2283            return f", {this_sql}"
    2284
    2285        if op_sql != "STRAIGHT_JOIN":
    2286            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2287
    2288        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
    2289
    2290    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    2291        args = self.expressions(expression, flat=True)
    2292        args = f"({args})" if len(args.split(",")) > 1 else args
    2293        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    2294
    2295    def lateral_op(self, expression: exp.Lateral) -> str:
    2296        cross_apply = expression.args.get("cross_apply")
    2297
    2298        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2299        if cross_apply is True:
    2300            op = "INNER JOIN "
    2301        elif cross_apply is False:
    2302            op = "LEFT JOIN "
    2303        else:
    2304            op = ""
    2305
    2306        return f"{op}LATERAL"
    2307
    2308    def lateral_sql(self, expression: exp.Lateral) -> str:
    2309        this = self.sql(expression, "this")
    2310
    2311        if expression.args.get("view"):
    2312            alias = expression.args["alias"]
    2313            columns = self.expressions(alias, key="columns", flat=True)
    2314            table = f" {alias.name}" if alias.name else ""
    2315            columns = f" AS {columns}" if columns else ""
    2316            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2317            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2318
    2319        alias = self.sql(expression, "alias")
    2320        alias = f" AS {alias}" if alias else ""
    2321
    2322        ordinality = expression.args.get("ordinality") or ""
    2323        if ordinality:
    2324            ordinality = f" WITH ORDINALITY{alias}"
    2325            alias = ""
    2326
    2327        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
    2328
    2329    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2330        this = self.sql(expression, "this")
    2331
    2332        args = [
    2333            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2334            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2335            if e
    2336        ]
    2337
    2338        args_sql = ", ".join(self.sql(e) for e in args)
    2339        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2340        expressions = self.expressions(expression, flat=True)
    2341        limit_options = self.sql(expression, "limit_options")
    2342        expressions = f" BY {expressions}" if expressions else ""
    2343
    2344        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
    2345
    2346    def offset_sql(self, expression: exp.Offset) -> str:
    2347        this = self.sql(expression, "this")
    2348        value = expression.expression
    2349        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2350        expressions = self.expressions(expression, flat=True)
    2351        expressions = f" BY {expressions}" if expressions else ""
    2352        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
    2353
    2354    def setitem_sql(self, expression: exp.SetItem) -> str:
    2355        kind = self.sql(expression, "kind")
    2356        kind = f"{kind} " if kind else ""
    2357        this = self.sql(expression, "this")
    2358        expressions = self.expressions(expression)
    2359        collate = self.sql(expression, "collate")
    2360        collate = f" COLLATE {collate}" if collate else ""
    2361        global_ = "GLOBAL " if expression.args.get("global") else ""
    2362        return f"{global_}{kind}{this}{expressions}{collate}"
    2363
    2364    def set_sql(self, expression: exp.Set) -> str:
    2365        expressions = f" {self.expressions(expression, flat=True)}"
    2366        tag = " TAG" if expression.args.get("tag") else ""
    2367        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
    2368
    2369    def pragma_sql(self, expression: exp.Pragma) -> str:
    2370        return f"PRAGMA {self.sql(expression, 'this')}"
    2371
    2372    def lock_sql(self, expression: exp.Lock) -> str:
    2373        if not self.LOCKING_READS_SUPPORTED:
    2374            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2375            return ""
    2376
    2377        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2378        expressions = self.expressions(expression, flat=True)
    2379        expressions = f" OF {expressions}" if expressions else ""
    2380        wait = expression.args.get("wait")
    2381
    2382        if wait is not None:
    2383            if isinstance(wait, exp.Literal):
    2384                wait = f" WAIT {self.sql(wait)}"
    2385            else:
    2386                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2387
    2388        return f"{lock_type}{expressions}{wait or ''}"
    2389
    2390    def literal_sql(self, expression: exp.Literal) -> str:
    2391        text = expression.this or ""
    2392        if expression.is_string:
    2393            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2394        return text
    2395
    2396    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2397        if self.dialect.ESCAPED_SEQUENCES:
    2398            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2399            text = "".join(
    2400                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2401            )
    2402
    2403        return self._replace_line_breaks(text).replace(
    2404            self.dialect.QUOTE_END, self._escaped_quote_end
    2405        )
    2406
    2407    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2408        local = " LOCAL" if expression.args.get("local") else ""
    2409        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2410        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2411        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2412        partition = self.sql(expression, "partition")
    2413        partition = f" {partition}" if partition else ""
    2414        input_format = self.sql(expression, "input_format")
    2415        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2416        serde = self.sql(expression, "serde")
    2417        serde = f" SERDE {serde}" if serde else ""
    2418        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    2419
    2420    def null_sql(self, *_) -> str:
    2421        return "NULL"
    2422
    2423    def boolean_sql(self, expression: exp.Boolean) -> str:
    2424        return "TRUE" if expression.this else "FALSE"
    2425
    2426    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2427        this = self.sql(expression, "this")
    2428        this = f"{this} " if this else this
    2429        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2430        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2431
    2432    def withfill_sql(self, expression: exp.WithFill) -> str:
    2433        from_sql = self.sql(expression, "from")
    2434        from_sql = f" FROM {from_sql}" if from_sql else ""
    2435        to_sql = self.sql(expression, "to")
    2436        to_sql = f" TO {to_sql}" if to_sql else ""
    2437        step_sql = self.sql(expression, "step")
    2438        step_sql = f" STEP {step_sql}" if step_sql else ""
    2439        interpolated_values = [
    2440            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2441            if isinstance(e, exp.Alias)
    2442            else self.sql(e, "this")
    2443            for e in expression.args.get("interpolate") or []
    2444        ]
    2445        interpolate = (
    2446            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2447        )
    2448        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
    2449
    2450    def cluster_sql(self, expression: exp.Cluster) -> str:
    2451        return self.op_expressions("CLUSTER BY", expression)
    2452
    2453    def distribute_sql(self, expression: exp.Distribute) -> str:
    2454        return self.op_expressions("DISTRIBUTE BY", expression)
    2455
    2456    def sort_sql(self, expression: exp.Sort) -> str:
    2457        return self.op_expressions("SORT BY", expression)
    2458
    2459    def ordered_sql(self, expression: exp.Ordered) -> str:
    2460        desc = expression.args.get("desc")
    2461        asc = not desc
    2462
    2463        nulls_first = expression.args.get("nulls_first")
    2464        nulls_last = not nulls_first
    2465        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2466        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2467        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2468
    2469        this = self.sql(expression, "this")
    2470
    2471        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2472        nulls_sort_change = ""
    2473        if nulls_first and (
    2474            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2475        ):
    2476            nulls_sort_change = " NULLS FIRST"
    2477        elif (
    2478            nulls_last
    2479            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2480            and not nulls_are_last
    2481        ):
    2482            nulls_sort_change = " NULLS LAST"
    2483
    2484        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2485        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2486            window = expression.find_ancestor(exp.Window, exp.Select)
    2487            if isinstance(window, exp.Window) and window.args.get("spec"):
    2488                self.unsupported(
    2489                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2490                )
    2491                nulls_sort_change = ""
    2492            elif self.NULL_ORDERING_SUPPORTED is False and (
    2493                (asc and nulls_sort_change == " NULLS LAST")
    2494                or (desc and nulls_sort_change == " NULLS FIRST")
    2495            ):
    2496                # BigQuery does not allow these ordering/nulls combinations when used under
    2497                # an aggregation func or under a window containing one
    2498                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2499
    2500                if isinstance(ancestor, exp.Window):
    2501                    ancestor = ancestor.this
    2502                if isinstance(ancestor, exp.AggFunc):
    2503                    self.unsupported(
    2504                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2505                    )
    2506                    nulls_sort_change = ""
    2507            elif self.NULL_ORDERING_SUPPORTED is None:
    2508                if expression.this.is_int:
    2509                    self.unsupported(
    2510                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2511                    )
    2512                elif not isinstance(expression.this, exp.Rand):
    2513                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2514                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2515                nulls_sort_change = ""
    2516
    2517        with_fill = self.sql(expression, "with_fill")
    2518        with_fill = f" {with_fill}" if with_fill else ""
    2519
    2520        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
    2521
    2522    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2523        window_frame = self.sql(expression, "window_frame")
    2524        window_frame = f"{window_frame} " if window_frame else ""
    2525
    2526        this = self.sql(expression, "this")
    2527
    2528        return f"{window_frame}{this}"
    2529
    2530    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2531        partition = self.partition_by_sql(expression)
    2532        order = self.sql(expression, "order")
    2533        measures = self.expressions(expression, key="measures")
    2534        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2535        rows = self.sql(expression, "rows")
    2536        rows = self.seg(rows) if rows else ""
    2537        after = self.sql(expression, "after")
    2538        after = self.seg(after) if after else ""
    2539        pattern = self.sql(expression, "pattern")
    2540        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2541        definition_sqls = [
    2542            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2543            for definition in expression.args.get("define", [])
    2544        ]
    2545        definitions = self.expressions(sqls=definition_sqls)
    2546        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2547        body = "".join(
    2548            (
    2549                partition,
    2550                order,
    2551                measures,
    2552                rows,
    2553                after,
    2554                pattern,
    2555                define,
    2556            )
    2557        )
    2558        alias = self.sql(expression, "alias")
    2559        alias = f" {alias}" if alias else ""
    2560        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    2561
    2562    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2563        limit = expression.args.get("limit")
    2564
    2565        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2566            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2567        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2568            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2569
    2570        return csv(
    2571            *sqls,
    2572            *[self.sql(join) for join in expression.args.get("joins") or []],
    2573            self.sql(expression, "match"),
    2574            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2575            self.sql(expression, "prewhere"),
    2576            self.sql(expression, "where"),
    2577            self.sql(expression, "connect"),
    2578            self.sql(expression, "group"),
    2579            self.sql(expression, "having"),
    2580            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2581            self.sql(expression, "order"),
    2582            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2583            *self.after_limit_modifiers(expression),
    2584            self.options_modifier(expression),
    2585            sep="",
    2586        )
    2587
    2588    def options_modifier(self, expression: exp.Expression) -> str:
    2589        options = self.expressions(expression, key="options")
    2590        return f" {options}" if options else ""
    2591
    2592    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2593        self.unsupported("Unsupported query option.")
    2594        return ""
    2595
    2596    def offset_limit_modifiers(
    2597        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2598    ) -> t.List[str]:
    2599        return [
    2600            self.sql(expression, "offset") if fetch else self.sql(limit),
    2601            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2602        ]
    2603
    2604    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    2605        locks = self.expressions(expression, key="locks", sep=" ")
    2606        locks = f" {locks}" if locks else ""
    2607        return [locks, self.sql(expression, "sample")]
    2608
    2609    def select_sql(self, expression: exp.Select) -> str:
    2610        into = expression.args.get("into")
    2611        if not self.SUPPORTS_SELECT_INTO and into:
    2612            into.pop()
    2613
    2614        hint = self.sql(expression, "hint")
    2615        distinct = self.sql(expression, "distinct")
    2616        distinct = f" {distinct}" if distinct else ""
    2617        kind = self.sql(expression, "kind")
    2618
    2619        limit = expression.args.get("limit")
    2620        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2621            top = self.limit_sql(limit, top=True)
    2622            limit.pop()
    2623        else:
    2624            top = ""
    2625
    2626        expressions = self.expressions(expression)
    2627
    2628        if kind:
    2629            if kind in self.SELECT_KINDS:
    2630                kind = f" AS {kind}"
    2631            else:
    2632                if kind == "STRUCT":
    2633                    expressions = self.expressions(
    2634                        sqls=[
    2635                            self.sql(
    2636                                exp.Struct(
    2637                                    expressions=[
    2638                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2639                                        if isinstance(e, exp.Alias)
    2640                                        else e
    2641                                        for e in expression.expressions
    2642                                    ]
    2643                                )
    2644                            )
    2645                        ]
    2646                    )
    2647                kind = ""
    2648
    2649        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2650        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2651
    2652        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2653        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2654        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2655        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2656        sql = self.query_modifiers(
    2657            expression,
    2658            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2659            self.sql(expression, "into", comment=False),
    2660            self.sql(expression, "from", comment=False),
    2661        )
    2662
    2663        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2664        if expression.args.get("with"):
    2665            sql = self.maybe_comment(sql, expression)
    2666            expression.pop_comments()
    2667
    2668        sql = self.prepend_ctes(expression, sql)
    2669
    2670        if not self.SUPPORTS_SELECT_INTO and into:
    2671            if into.args.get("temporary"):
    2672                table_kind = " TEMPORARY"
    2673            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2674                table_kind = " UNLOGGED"
    2675            else:
    2676                table_kind = ""
    2677            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2678
    2679        return sql
    2680
    2681    def schema_sql(self, expression: exp.Schema) -> str:
    2682        this = self.sql(expression, "this")
    2683        sql = self.schema_columns_sql(expression)
    2684        return f"{this} {sql}" if this and sql else this or sql
    2685
    2686    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2687        if expression.expressions:
    2688            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2689        return ""
    2690
    2691    def star_sql(self, expression: exp.Star) -> str:
    2692        except_ = self.expressions(expression, key="except", flat=True)
    2693        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2694        replace = self.expressions(expression, key="replace", flat=True)
    2695        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2696        rename = self.expressions(expression, key="rename", flat=True)
    2697        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2698        return f"*{except_}{replace}{rename}"
    2699
    2700    def parameter_sql(self, expression: exp.Parameter) -> str:
    2701        this = self.sql(expression, "this")
    2702        return f"{self.PARAMETER_TOKEN}{this}"
    2703
    2704    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2705        this = self.sql(expression, "this")
    2706        kind = expression.text("kind")
    2707        if kind:
    2708            kind = f"{kind}."
    2709        return f"@@{kind}{this}"
    2710
    2711    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2712        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
    2713
    2714    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2715        alias = self.sql(expression, "alias")
    2716        alias = f"{sep}{alias}" if alias else ""
    2717        sample = self.sql(expression, "sample")
    2718        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2719            alias = f"{sample}{alias}"
    2720
    2721            # Set to None so it's not generated again by self.query_modifiers()
    2722            expression.set("sample", None)
    2723
    2724        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2725        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2726        return self.prepend_ctes(expression, sql)
    2727
    2728    def qualify_sql(self, expression: exp.Qualify) -> str:
    2729        this = self.indent(self.sql(expression, "this"))
    2730        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    2731
    2732    def unnest_sql(self, expression: exp.Unnest) -> str:
    2733        args = self.expressions(expression, flat=True)
    2734
    2735        alias = expression.args.get("alias")
    2736        offset = expression.args.get("offset")
    2737
    2738        if self.UNNEST_WITH_ORDINALITY:
    2739            if alias and isinstance(offset, exp.Expression):
    2740                alias.append("columns", offset)
    2741
    2742        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2743            columns = alias.columns
    2744            alias = self.sql(columns[0]) if columns else ""
    2745        else:
    2746            alias = self.sql(alias)
    2747
    2748        alias = f" AS {alias}" if alias else alias
    2749        if self.UNNEST_WITH_ORDINALITY:
    2750            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2751        else:
    2752            if isinstance(offset, exp.Expression):
    2753                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2754            elif offset:
    2755                suffix = f"{alias} WITH OFFSET"
    2756            else:
    2757                suffix = alias
    2758
    2759        return f"UNNEST({args}){suffix}"
    2760
    2761    def prewhere_sql(self, expression: exp.PreWhere) -> str:
    2762        return ""
    2763
    2764    def where_sql(self, expression: exp.Where) -> str:
    2765        this = self.indent(self.sql(expression, "this"))
    2766        return f"{self.seg('WHERE')}{self.sep()}{this}"
    2767
    2768    def window_sql(self, expression: exp.Window) -> str:
    2769        this = self.sql(expression, "this")
    2770        partition = self.partition_by_sql(expression)
    2771        order = expression.args.get("order")
    2772        order = self.order_sql(order, flat=True) if order else ""
    2773        spec = self.sql(expression, "spec")
    2774        alias = self.sql(expression, "alias")
    2775        over = self.sql(expression, "over") or "OVER"
    2776
    2777        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2778
    2779        first = expression.args.get("first")
    2780        if first is None:
    2781            first = ""
    2782        else:
    2783            first = "FIRST" if first else "LAST"
    2784
    2785        if not partition and not order and not spec and alias:
    2786            return f"{this} {alias}"
    2787
    2788        args = self.format_args(
    2789            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
    2790        )
    2791        return f"{this} ({args})"
    2792
    2793    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2794        partition = self.expressions(expression, key="partition_by", flat=True)
    2795        return f"PARTITION BY {partition}" if partition else ""
    2796
    2797    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2798        kind = self.sql(expression, "kind")
    2799        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2800        end = (
    2801            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2802            or "CURRENT ROW"
    2803        )
    2804        return f"{kind} BETWEEN {start} AND {end}"
    2805
    2806    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2807        this = self.sql(expression, "this")
    2808        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2809        return f"{this} WITHIN GROUP ({expression_sql})"
    2810
    2811    def between_sql(self, expression: exp.Between) -> str:
    2812        this = self.sql(expression, "this")
    2813        low = self.sql(expression, "low")
    2814        high = self.sql(expression, "high")
    2815        return f"{this} BETWEEN {low} AND {high}"
    2816
    2817    def bracket_offset_expressions(
    2818        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2819    ) -> t.List[exp.Expression]:
    2820        return apply_index_offset(
    2821            expression.this,
    2822            expression.expressions,
    2823            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2824            dialect=self.dialect,
    2825        )
    2826
    2827    def bracket_sql(self, expression: exp.Bracket) -> str:
    2828        expressions = self.bracket_offset_expressions(expression)
    2829        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2830        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    2831
    2832    def all_sql(self, expression: exp.All) -> str:
    2833        return f"ALL {self.wrap(expression)}"
    2834
    2835    def any_sql(self, expression: exp.Any) -> str:
    2836        this = self.sql(expression, "this")
    2837        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2838            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2839                this = self.wrap(this)
    2840            return f"ANY{this}"
    2841        return f"ANY {this}"
    2842
    2843    def exists_sql(self, expression: exp.Exists) -> str:
    2844        return f"EXISTS{self.wrap(expression)}"
    2845
    2846    def case_sql(self, expression: exp.Case) -> str:
    2847        this = self.sql(expression, "this")
    2848        statements = [f"CASE {this}" if this else "CASE"]
    2849
    2850        for e in expression.args["ifs"]:
    2851            statements.append(f"WHEN {self.sql(e, 'this')}")
    2852            statements.append(f"THEN {self.sql(e, 'true')}")
    2853
    2854        default = self.sql(expression, "default")
    2855
    2856        if default:
    2857            statements.append(f"ELSE {default}")
    2858
    2859        statements.append("END")
    2860
    2861        if self.pretty and self.too_wide(statements):
    2862            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2863
    2864        return " ".join(statements)
    2865
    2866    def constraint_sql(self, expression: exp.Constraint) -> str:
    2867        this = self.sql(expression, "this")
    2868        expressions = self.expressions(expression, flat=True)
    2869        return f"CONSTRAINT {this} {expressions}"
    2870
    2871    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2872        order = expression.args.get("order")
    2873        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2874        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    2875
    2876    def extract_sql(self, expression: exp.Extract) -> str:
    2877        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2878        expression_sql = self.sql(expression, "expression")
    2879        return f"EXTRACT({this} FROM {expression_sql})"
    2880
    2881    def trim_sql(self, expression: exp.Trim) -> str:
    2882        trim_type = self.sql(expression, "position")
    2883
    2884        if trim_type == "LEADING":
    2885            func_name = "LTRIM"
    2886        elif trim_type == "TRAILING":
    2887            func_name = "RTRIM"
    2888        else:
    2889            func_name = "TRIM"
    2890
    2891        return self.func(func_name, expression.this, expression.expression)
    2892
    2893    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2894        args = expression.expressions
    2895        if isinstance(expression, exp.ConcatWs):
    2896            args = args[1:]  # Skip the delimiter
    2897
    2898        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2899            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2900
    2901        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2902            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2903
    2904        return args
    2905
    2906    def concat_sql(self, expression: exp.Concat) -> str:
    2907        expressions = self.convert_concat_args(expression)
    2908
    2909        # Some dialects don't allow a single-argument CONCAT call
    2910        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2911            return self.sql(expressions[0])
    2912
    2913        return self.func("CONCAT", *expressions)
    2914
    2915    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2916        return self.func(
    2917            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2918        )
    2919
    2920    def check_sql(self, expression: exp.Check) -> str:
    2921        this = self.sql(expression, key="this")
    2922        return f"CHECK ({this})"
    2923
    2924    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2925        expressions = self.expressions(expression, flat=True)
    2926        expressions = f" ({expressions})" if expressions else ""
    2927        reference = self.sql(expression, "reference")
    2928        reference = f" {reference}" if reference else ""
    2929        delete = self.sql(expression, "delete")
    2930        delete = f" ON DELETE {delete}" if delete else ""
    2931        update = self.sql(expression, "update")
    2932        update = f" ON UPDATE {update}" if update else ""
    2933        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2934        options = f" {options}" if options else ""
    2935        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
    2936
    2937    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2938        expressions = self.expressions(expression, flat=True)
    2939        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2940        options = f" {options}" if options else ""
    2941        return f"PRIMARY KEY ({expressions}){options}"
    2942
    2943    def if_sql(self, expression: exp.If) -> str:
    2944        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
    2945
    2946    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2947        modifier = expression.args.get("modifier")
    2948        modifier = f" {modifier}" if modifier else ""
    2949        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    2950
    2951    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2952        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
    2953
    2954    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2955        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2956
    2957        if expression.args.get("escape"):
    2958            path = self.escape_str(path)
    2959
    2960        if self.QUOTE_JSON_PATH:
    2961            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2962
    2963        return path
    2964
    2965    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2966        if isinstance(expression, exp.JSONPathPart):
    2967            transform = self.TRANSFORMS.get(expression.__class__)
    2968            if not callable(transform):
    2969                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2970                return ""
    2971
    2972            return transform(self, expression)
    2973
    2974        if isinstance(expression, int):
    2975            return str(expression)
    2976
    2977        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2978            escaped = expression.replace("'", "\\'")
    2979            escaped = f"\\'{expression}\\'"
    2980        else:
    2981            escaped = expression.replace('"', '\\"')
    2982            escaped = f'"{escaped}"'
    2983
    2984        return escaped
    2985
    2986    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2987        return f"{self.sql(expression, 'this')} FORMAT JSON"
    2988
    2989    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2990        null_handling = expression.args.get("null_handling")
    2991        null_handling = f" {null_handling}" if null_handling else ""
    2992
    2993        unique_keys = expression.args.get("unique_keys")
    2994        if unique_keys is not None:
    2995            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2996        else:
    2997            unique_keys = ""
    2998
    2999        return_type = self.sql(expression, "return_type")
    3000        return_type = f" RETURNING {return_type}" if return_type else ""
    3001        encoding = self.sql(expression, "encoding")
    3002        encoding = f" ENCODING {encoding}" if encoding else ""
    3003
    3004        return self.func(
    3005            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    3006            *expression.expressions,
    3007            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    3008        )
    3009
    3010    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
    3011        return self.jsonobject_sql(expression)
    3012
    3013    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    3014        null_handling = expression.args.get("null_handling")
    3015        null_handling = f" {null_handling}" if null_handling else ""
    3016        return_type = self.sql(expression, "return_type")
    3017        return_type = f" RETURNING {return_type}" if return_type else ""
    3018        strict = " STRICT" if expression.args.get("strict") else ""
    3019        return self.func(
    3020            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    3021        )
    3022
    3023    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    3024        this = self.sql(expression, "this")
    3025        order = self.sql(expression, "order")
    3026        null_handling = expression.args.get("null_handling")
    3027        null_handling = f" {null_handling}" if null_handling else ""
    3028        return_type = self.sql(expression, "return_type")
    3029        return_type = f" RETURNING {return_type}" if return_type else ""
    3030        strict = " STRICT" if expression.args.get("strict") else ""
    3031        return self.func(
    3032            "JSON_ARRAYAGG",
    3033            this,
    3034            suffix=f"{order}{null_handling}{return_type}{strict})",
    3035        )
    3036
    3037    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    3038        path = self.sql(expression, "path")
    3039        path = f" PATH {path}" if path else ""
    3040        nested_schema = self.sql(expression, "nested_schema")
    3041
    3042        if nested_schema:
    3043            return f"NESTED{path} {nested_schema}"
    3044
    3045        this = self.sql(expression, "this")
    3046        kind = self.sql(expression, "kind")
    3047        kind = f" {kind}" if kind else ""
    3048        return f"{this}{kind}{path}"
    3049
    3050    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    3051        return self.func("COLUMNS", *expression.expressions)
    3052
    3053    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    3054        this = self.sql(expression, "this")
    3055        path = self.sql(expression, "path")
    3056        path = f", {path}" if path else ""
    3057        error_handling = expression.args.get("error_handling")
    3058        error_handling = f" {error_handling}" if error_handling else ""
    3059        empty_handling = expression.args.get("empty_handling")
    3060        empty_handling = f" {empty_handling}" if empty_handling else ""
    3061        schema = self.sql(expression, "schema")
    3062        return self.func(
    3063            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    3064        )
    3065
    3066    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    3067        this = self.sql(expression, "this")
    3068        kind = self.sql(expression, "kind")
    3069        path = self.sql(expression, "path")
    3070        path = f" {path}" if path else ""
    3071        as_json = " AS JSON" if expression.args.get("as_json") else ""
    3072        return f"{this} {kind}{path}{as_json}"
    3073
    3074    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    3075        this = self.sql(expression, "this")
    3076        path = self.sql(expression, "path")
    3077        path = f", {path}" if path else ""
    3078        expressions = self.expressions(expression)
    3079        with_ = (
    3080            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    3081            if expressions
    3082            else ""
    3083        )
    3084        return f"OPENJSON({this}{path}){with_}"
    3085
    3086    def in_sql(self, expression: exp.In) -> str:
    3087        query = expression.args.get("query")
    3088        unnest = expression.args.get("unnest")
    3089        field = expression.args.get("field")
    3090        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3091
    3092        if query:
    3093            in_sql = self.sql(query)
    3094        elif unnest:
    3095            in_sql = self.in_unnest_op(unnest)
    3096        elif field:
    3097            in_sql = self.sql(field)
    3098        else:
    3099            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    3100
    3101        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    3102
    3103    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    3104        return f"(SELECT {self.sql(unnest)})"
    3105
    3106    def interval_sql(self, expression: exp.Interval) -> str:
    3107        unit = self.sql(expression, "unit")
    3108        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3109            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3110        unit = f" {unit}" if unit else ""
    3111
    3112        if self.SINGLE_STRING_INTERVAL:
    3113            this = expression.this.name if expression.this else ""
    3114            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    3115
    3116        this = self.sql(expression, "this")
    3117        if this:
    3118            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3119            this = f" {this}" if unwrapped else f" ({this})"
    3120
    3121        return f"INTERVAL{this}{unit}"
    3122
    3123    def return_sql(self, expression: exp.Return) -> str:
    3124        return f"RETURN {self.sql(expression, 'this')}"
    3125
    3126    def reference_sql(self, expression: exp.Reference) -> str:
    3127        this = self.sql(expression, "this")
    3128        expressions = self.expressions(expression, flat=True)
    3129        expressions = f"({expressions})" if expressions else ""
    3130        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3131        options = f" {options}" if options else ""
    3132        return f"REFERENCES {this}{expressions}{options}"
    3133
    3134    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3135        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3136        parent = expression.parent
    3137        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3138        return self.func(
    3139            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3140        )
    3141
    3142    def paren_sql(self, expression: exp.Paren) -> str:
    3143        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    3144        return f"({sql}{self.seg(')', sep='')}"
    3145
    3146    def neg_sql(self, expression: exp.Neg) -> str:
    3147        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    3148        this_sql = self.sql(expression, "this")
    3149        sep = " " if this_sql[0] == "-" else ""
    3150        return f"-{sep}{this_sql}"
    3151
    3152    def not_sql(self, expression: exp.Not) -> str:
    3153        return f"NOT {self.sql(expression, 'this')}"
    3154
    3155    def alias_sql(self, expression: exp.Alias) -> str:
    3156        alias = self.sql(expression, "alias")
    3157        alias = f" AS {alias}" if alias else ""
    3158        return f"{self.sql(expression, 'this')}{alias}"
    3159
    3160    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3161        alias = expression.args["alias"]
    3162
    3163        parent = expression.parent
    3164        pivot = parent and parent.parent
    3165
    3166        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
    3167            identifier_alias = isinstance(alias, exp.Identifier)
    3168            literal_alias = isinstance(alias, exp.Literal)
    3169
    3170            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3171                alias.replace(exp.Literal.string(alias.output_name))
    3172            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3173                alias.replace(exp.to_identifier(alias.output_name))
    3174
    3175        return self.alias_sql(expression)
    3176
    3177    def aliases_sql(self, expression: exp.Aliases) -> str:
    3178        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    3179
    3180    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3181        this = self.sql(expression, "this")
    3182        index = self.sql(expression, "expression")
    3183        return f"{this} AT {index}"
    3184
    3185    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3186        this = self.sql(expression, "this")
    3187        zone = self.sql(expression, "zone")
    3188        return f"{this} AT TIME ZONE {zone}"
    3189
    3190    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3191        this = self.sql(expression, "this")
    3192        zone = self.sql(expression, "zone")
    3193        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
    3194
    3195    def add_sql(self, expression: exp.Add) -> str:
    3196        return self.binary(expression, "+")
    3197
    3198    def and_sql(
    3199        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3200    ) -> str:
    3201        return self.connector_sql(expression, "AND", stack)
    3202
    3203    def or_sql(
    3204        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3205    ) -> str:
    3206        return self.connector_sql(expression, "OR", stack)
    3207
    3208    def xor_sql(
    3209        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3210    ) -> str:
    3211        return self.connector_sql(expression, "XOR", stack)
    3212
    3213    def connector_sql(
    3214        self,
    3215        expression: exp.Connector,
    3216        op: str,
    3217        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3218    ) -> str:
    3219        if stack is not None:
    3220            if expression.expressions:
    3221                stack.append(self.expressions(expression, sep=f" {op} "))
    3222            else:
    3223                stack.append(expression.right)
    3224                if expression.comments and self.comments:
    3225                    for comment in expression.comments:
    3226                        if comment:
    3227                            op += f" /*{self.pad_comment(comment)}*/"
    3228                stack.extend((op, expression.left))
    3229            return op
    3230
    3231        stack = [expression]
    3232        sqls: t.List[str] = []
    3233        ops = set()
    3234
    3235        while stack:
    3236            node = stack.pop()
    3237            if isinstance(node, exp.Connector):
    3238                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3239            else:
    3240                sql = self.sql(node)
    3241                if sqls and sqls[-1] in ops:
    3242                    sqls[-1] += f" {sql}"
    3243                else:
    3244                    sqls.append(sql)
    3245
    3246        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3247        return sep.join(sqls)
    3248
    3249    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    3250        return self.binary(expression, "&")
    3251
    3252    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    3253        return self.binary(expression, "<<")
    3254
    3255    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    3256        return f"~{self.sql(expression, 'this')}"
    3257
    3258    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    3259        return self.binary(expression, "|")
    3260
    3261    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    3262        return self.binary(expression, ">>")
    3263
    3264    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    3265        return self.binary(expression, "^")
    3266
    3267    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3268        format_sql = self.sql(expression, "format")
    3269        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3270        to_sql = self.sql(expression, "to")
    3271        to_sql = f" {to_sql}" if to_sql else ""
    3272        action = self.sql(expression, "action")
    3273        action = f" {action}" if action else ""
    3274        default = self.sql(expression, "default")
    3275        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3276        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
    3277
    3278    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    3279        zone = self.sql(expression, "this")
    3280        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    3281
    3282    def collate_sql(self, expression: exp.Collate) -> str:
    3283        if self.COLLATE_IS_FUNC:
    3284            return self.function_fallback_sql(expression)
    3285        return self.binary(expression, "COLLATE")
    3286
    3287    def command_sql(self, expression: exp.Command) -> str:
    3288        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
    3289
    3290    def comment_sql(self, expression: exp.Comment) -> str:
    3291        this = self.sql(expression, "this")
    3292        kind = expression.args["kind"]
    3293        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3294        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3295        expression_sql = self.sql(expression, "expression")
    3296        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
    3297
    3298    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3299        this = self.sql(expression, "this")
    3300        delete = " DELETE" if expression.args.get("delete") else ""
    3301        recompress = self.sql(expression, "recompress")
    3302        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3303        to_disk = self.sql(expression, "to_disk")
    3304        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3305        to_volume = self.sql(expression, "to_volume")
    3306        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3307        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    3308
    3309    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3310        where = self.sql(expression, "where")
    3311        group = self.sql(expression, "group")
    3312        aggregates = self.expressions(expression, key="aggregates")
    3313        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3314
    3315        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3316            return f"TTL {self.expressions(expression, flat=True)}"
    3317
    3318        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    3319
    3320    def transaction_sql(self, expression: exp.Transaction) -> str:
    3321        return "BEGIN"
    3322
    3323    def commit_sql(self, expression: exp.Commit) -> str:
    3324        chain = expression.args.get("chain")
    3325        if chain is not None:
    3326            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3327
    3328        return f"COMMIT{chain or ''}"
    3329
    3330    def rollback_sql(self, expression: exp.Rollback) -> str:
    3331        savepoint = expression.args.get("savepoint")
    3332        savepoint = f" TO {savepoint}" if savepoint else ""
    3333        return f"ROLLBACK{savepoint}"
    3334
    3335    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3336        this = self.sql(expression, "this")
    3337
    3338        dtype = self.sql(expression, "dtype")
    3339        if dtype:
    3340            collate = self.sql(expression, "collate")
    3341            collate = f" COLLATE {collate}" if collate else ""
    3342            using = self.sql(expression, "using")
    3343            using = f" USING {using}" if using else ""
    3344            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
    3345
    3346        default = self.sql(expression, "default")
    3347        if default:
    3348            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3349
    3350        comment = self.sql(expression, "comment")
    3351        if comment:
    3352            return f"ALTER COLUMN {this} COMMENT {comment}"
    3353
    3354        visible = expression.args.get("visible")
    3355        if visible:
    3356            return f"ALTER COLUMN {this} SET {visible}"
    3357
    3358        allow_null = expression.args.get("allow_null")
    3359        drop = expression.args.get("drop")
    3360
    3361        if not drop and not allow_null:
    3362            self.unsupported("Unsupported ALTER COLUMN syntax")
    3363
    3364        if allow_null is not None:
    3365            keyword = "DROP" if drop else "SET"
    3366            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3367
    3368        return f"ALTER COLUMN {this} DROP DEFAULT"
    3369
    3370    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
    3371        this = self.sql(expression, "this")
    3372
    3373        visible = expression.args.get("visible")
    3374        visible_sql = "VISIBLE" if visible else "INVISIBLE"
    3375
    3376        return f"ALTER INDEX {this} {visible_sql}"
    3377
    3378    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3379        this = self.sql(expression, "this")
    3380        if not isinstance(expression.this, exp.Var):
    3381            this = f"KEY DISTKEY {this}"
    3382        return f"ALTER DISTSTYLE {this}"
    3383
    3384    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3385        compound = " COMPOUND" if expression.args.get("compound") else ""
    3386        this = self.sql(expression, "this")
    3387        expressions = self.expressions(expression, flat=True)
    3388        expressions = f"({expressions})" if expressions else ""
    3389        return f"ALTER{compound} SORTKEY {this or expressions}"
    3390
    3391    def alterrename_sql(self, expression: exp.AlterRename) -> str:
    3392        if not self.RENAME_TABLE_WITH_DB:
    3393            # Remove db from tables
    3394            expression = expression.transform(
    3395                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3396            ).assert_is(exp.AlterRename)
    3397        this = self.sql(expression, "this")
    3398        return f"RENAME TO {this}"
    3399
    3400    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3401        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3402        old_column = self.sql(expression, "this")
    3403        new_column = self.sql(expression, "to")
    3404        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
    3405
    3406    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3407        exprs = self.expressions(expression, flat=True)
    3408        return f"SET {exprs}"
    3409
    3410    def alter_sql(self, expression: exp.Alter) -> str:
    3411        actions = expression.args["actions"]
    3412
    3413        if isinstance(actions[0], exp.ColumnDef):
    3414            actions = self.add_column_sql(expression)
    3415        elif isinstance(actions[0], exp.Schema):
    3416            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3417        elif isinstance(actions[0], exp.Delete):
    3418            actions = self.expressions(expression, key="actions", flat=True)
    3419        elif isinstance(actions[0], exp.Query):
    3420            actions = "AS " + self.expressions(expression, key="actions")
    3421        else:
    3422            actions = self.expressions(expression, key="actions", flat=True)
    3423
    3424        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3425        on_cluster = self.sql(expression, "cluster")
    3426        on_cluster = f" {on_cluster}" if on_cluster else ""
    3427        only = " ONLY" if expression.args.get("only") else ""
    3428        options = self.expressions(expression, key="options")
    3429        options = f", {options}" if options else ""
    3430        kind = self.sql(expression, "kind")
    3431        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3432
    3433        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
    3434
    3435    def add_column_sql(self, expression: exp.Alter) -> str:
    3436        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3437            return self.expressions(
    3438                expression,
    3439                key="actions",
    3440                prefix="ADD COLUMN ",
    3441                skip_first=True,
    3442            )
    3443        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
    3444
    3445    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3446        expressions = self.expressions(expression)
    3447        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3448        return f"DROP{exists}{expressions}"
    3449
    3450    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3451        return f"ADD {self.expressions(expression)}"
    3452
    3453    def distinct_sql(self, expression: exp.Distinct) -> str:
    3454        this = self.expressions(expression, flat=True)
    3455
    3456        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3457            case = exp.case()
    3458            for arg in expression.expressions:
    3459                case = case.when(arg.is_(exp.null()), exp.null())
    3460            this = self.sql(case.else_(f"({this})"))
    3461
    3462        this = f" {this}" if this else ""
    3463
    3464        on = self.sql(expression, "on")
    3465        on = f" ON {on}" if on else ""
    3466        return f"DISTINCT{this}{on}"
    3467
    3468    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    3469        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
    3470
    3471    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    3472        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
    3473
    3474    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3475        this_sql = self.sql(expression, "this")
    3476        expression_sql = self.sql(expression, "expression")
    3477        kind = "MAX" if expression.args.get("max") else "MIN"
    3478        return f"{this_sql} HAVING {kind} {expression_sql}"
    3479
    3480    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3481        return self.sql(
    3482            exp.Cast(
    3483                this=exp.Div(this=expression.this, expression=expression.expression),
    3484                to=exp.DataType(this=exp.DataType.Type.INT),
    3485            )
    3486        )
    3487
    3488    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3489        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3490            return self.func(
    3491                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3492            )
    3493        return self.binary(expression, "||")
    3494
    3495    def div_sql(self, expression: exp.Div) -> str:
    3496        l, r = expression.left, expression.right
    3497
    3498        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3499            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3500
    3501        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3502            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3503                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3504
    3505        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3506            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3507                return self.sql(
    3508                    exp.cast(
    3509                        l / r,
    3510                        to=exp.DataType.Type.BIGINT,
    3511                    )
    3512                )
    3513
    3514        return self.binary(expression, "/")
    3515
    3516    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3517        n = exp._wrap(expression.this, exp.Binary)
    3518        d = exp._wrap(expression.expression, exp.Binary)
    3519        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
    3520
    3521    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    3522        return self.binary(expression, "OVERLAPS")
    3523
    3524    def distance_sql(self, expression: exp.Distance) -> str:
    3525        return self.binary(expression, "<->")
    3526
    3527    def dot_sql(self, expression: exp.Dot) -> str:
    3528        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    3529
    3530    def eq_sql(self, expression: exp.EQ) -> str:
    3531        return self.binary(expression, "=")
    3532
    3533    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    3534        return self.binary(expression, ":=")
    3535
    3536    def escape_sql(self, expression: exp.Escape) -> str:
    3537        return self.binary(expression, "ESCAPE")
    3538
    3539    def glob_sql(self, expression: exp.Glob) -> str:
    3540        return self.binary(expression, "GLOB")
    3541
    3542    def gt_sql(self, expression: exp.GT) -> str:
    3543        return self.binary(expression, ">")
    3544
    3545    def gte_sql(self, expression: exp.GTE) -> str:
    3546        return self.binary(expression, ">=")
    3547
    3548    def ilike_sql(self, expression: exp.ILike) -> str:
    3549        return self.binary(expression, "ILIKE")
    3550
    3551    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    3552        return self.binary(expression, "ILIKE ANY")
    3553
    3554    def is_sql(self, expression: exp.Is) -> str:
    3555        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3556            return self.sql(
    3557                expression.this if expression.expression.this else exp.not_(expression.this)
    3558            )
    3559        return self.binary(expression, "IS")
    3560
    3561    def like_sql(self, expression: exp.Like) -> str:
    3562        return self.binary(expression, "LIKE")
    3563
    3564    def likeany_sql(self, expression: exp.LikeAny) -> str:
    3565        return self.binary(expression, "LIKE ANY")
    3566
    3567    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    3568        return self.binary(expression, "SIMILAR TO")
    3569
    3570    def lt_sql(self, expression: exp.LT) -> str:
    3571        return self.binary(expression, "<")
    3572
    3573    def lte_sql(self, expression: exp.LTE) -> str:
    3574        return self.binary(expression, "<=")
    3575
    3576    def mod_sql(self, expression: exp.Mod) -> str:
    3577        return self.binary(expression, "%")
    3578
    3579    def mul_sql(self, expression: exp.Mul) -> str:
    3580        return self.binary(expression, "*")
    3581
    3582    def neq_sql(self, expression: exp.NEQ) -> str:
    3583        return self.binary(expression, "<>")
    3584
    3585    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    3586        return self.binary(expression, "IS NOT DISTINCT FROM")
    3587
    3588    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    3589        return self.binary(expression, "IS DISTINCT FROM")
    3590
    3591    def slice_sql(self, expression: exp.Slice) -> str:
    3592        return self.binary(expression, ":")
    3593
    3594    def sub_sql(self, expression: exp.Sub) -> str:
    3595        return self.binary(expression, "-")
    3596
    3597    def trycast_sql(self, expression: exp.TryCast) -> str:
    3598        return self.cast_sql(expression, safe_prefix="TRY_")
    3599
    3600    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
    3601        return self.cast_sql(expression)
    3602
    3603    def try_sql(self, expression: exp.Try) -> str:
    3604        if not self.TRY_SUPPORTED:
    3605            self.unsupported("Unsupported TRY function")
    3606            return self.sql(expression, "this")
    3607
    3608        return self.func("TRY", expression.this)
    3609
    3610    def log_sql(self, expression: exp.Log) -> str:
    3611        this = expression.this
    3612        expr = expression.expression
    3613
    3614        if self.dialect.LOG_BASE_FIRST is False:
    3615            this, expr = expr, this
    3616        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3617            if this.name in ("2", "10"):
    3618                return self.func(f"LOG{this.name}", expr)
    3619
    3620            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3621
    3622        return self.func("LOG", this, expr)
    3623
    3624    def use_sql(self, expression: exp.Use) -> str:
    3625        kind = self.sql(expression, "kind")
    3626        kind = f" {kind}" if kind else ""
    3627        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
    3628        this = f" {this}" if this else ""
    3629        return f"USE{kind}{this}"
    3630
    3631    def binary(self, expression: exp.Binary, op: str) -> str:
    3632        sqls: t.List[str] = []
    3633        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3634        binary_type = type(expression)
    3635
    3636        while stack:
    3637            node = stack.pop()
    3638
    3639            if type(node) is binary_type:
    3640                op_func = node.args.get("operator")
    3641                if op_func:
    3642                    op = f"OPERATOR({self.sql(op_func)})"
    3643
    3644                stack.append(node.right)
    3645                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3646                stack.append(node.left)
    3647            else:
    3648                sqls.append(self.sql(node))
    3649
    3650        return "".join(sqls)
    3651
    3652    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3653        to_clause = self.sql(expression, "to")
    3654        if to_clause:
    3655            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3656
    3657        return self.function_fallback_sql(expression)
    3658
    3659    def function_fallback_sql(self, expression: exp.Func) -> str:
    3660        args = []
    3661
    3662        for key in expression.arg_types:
    3663            arg_value = expression.args.get(key)
    3664
    3665            if isinstance(arg_value, list):
    3666                for value in arg_value:
    3667                    args.append(value)
    3668            elif arg_value is not None:
    3669                args.append(arg_value)
    3670
    3671        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3672            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3673        else:
    3674            name = expression.sql_name()
    3675
    3676        return self.func(name, *args)
    3677
    3678    def func(
    3679        self,
    3680        name: str,
    3681        *args: t.Optional[exp.Expression | str],
    3682        prefix: str = "(",
    3683        suffix: str = ")",
    3684        normalize: bool = True,
    3685    ) -> str:
    3686        name = self.normalize_func(name) if normalize else name
    3687        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
    3688
    3689    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3690        arg_sqls = tuple(
    3691            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3692        )
    3693        if self.pretty and self.too_wide(arg_sqls):
    3694            return self.indent(
    3695                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3696            )
    3697        return sep.join(arg_sqls)
    3698
    3699    def too_wide(self, args: t.Iterable) -> bool:
    3700        return sum(len(arg) for arg in args) > self.max_text_width
    3701
    3702    def format_time(
    3703        self,
    3704        expression: exp.Expression,
    3705        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3706        inverse_time_trie: t.Optional[t.Dict] = None,
    3707    ) -> t.Optional[str]:
    3708        return format_time(
    3709            self.sql(expression, "format"),
    3710            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3711            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3712        )
    3713
    3714    def expressions(
    3715        self,
    3716        expression: t.Optional[exp.Expression] = None,
    3717        key: t.Optional[str] = None,
    3718        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3719        flat: bool = False,
    3720        indent: bool = True,
    3721        skip_first: bool = False,
    3722        skip_last: bool = False,
    3723        sep: str = ", ",
    3724        prefix: str = "",
    3725        dynamic: bool = False,
    3726        new_line: bool = False,
    3727    ) -> str:
    3728        expressions = expression.args.get(key or "expressions") if expression else sqls
    3729
    3730        if not expressions:
    3731            return ""
    3732
    3733        if flat:
    3734            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3735
    3736        num_sqls = len(expressions)
    3737        result_sqls = []
    3738
    3739        for i, e in enumerate(expressions):
    3740            sql = self.sql(e, comment=False)
    3741            if not sql:
    3742                continue
    3743
    3744            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3745
    3746            if self.pretty:
    3747                if self.leading_comma:
    3748                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3749                else:
    3750                    result_sqls.append(
    3751                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3752                    )
    3753            else:
    3754                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3755
    3756        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3757            if new_line:
    3758                result_sqls.insert(0, "")
    3759                result_sqls.append("")
    3760            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3761        else:
    3762            result_sql = "".join(result_sqls)
    3763
    3764        return (
    3765            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3766            if indent
    3767            else result_sql
    3768        )
    3769
    3770    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3771        flat = flat or isinstance(expression.parent, exp.Properties)
    3772        expressions_sql = self.expressions(expression, flat=flat)
    3773        if flat:
    3774            return f"{op} {expressions_sql}"
    3775        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    3776
    3777    def naked_property(self, expression: exp.Property) -> str:
    3778        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3779        if not property_name:
    3780            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3781        return f"{property_name} {self.sql(expression, 'this')}"
    3782
    3783    def tag_sql(self, expression: exp.Tag) -> str:
    3784        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    3785
    3786    def token_sql(self, token_type: TokenType) -> str:
    3787        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    3788
    3789    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3790        this = self.sql(expression, "this")
    3791        expressions = self.no_identify(self.expressions, expression)
    3792        expressions = (
    3793            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3794        )
    3795        return f"{this}{expressions}" if expressions.strip() != "" else this
    3796
    3797    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3798        this = self.sql(expression, "this")
    3799        expressions = self.expressions(expression, flat=True)
    3800        return f"{this}({expressions})"
    3801
    3802    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    3803        return self.binary(expression, "=>")
    3804
    3805    def when_sql(self, expression: exp.When) -> str:
    3806        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3807        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3808        condition = self.sql(expression, "condition")
    3809        condition = f" AND {condition}" if condition else ""
    3810
    3811        then_expression = expression.args.get("then")
    3812        if isinstance(then_expression, exp.Insert):
    3813            this = self.sql(then_expression, "this")
    3814            this = f"INSERT {this}" if this else "INSERT"
    3815            then = self.sql(then_expression, "expression")
    3816            then = f"{this} VALUES {then}" if then else this
    3817        elif isinstance(then_expression, exp.Update):
    3818            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3819                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3820            else:
    3821                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3822        else:
    3823            then = self.sql(then_expression)
    3824        return f"WHEN {matched}{source}{condition} THEN {then}"
    3825
    3826    def whens_sql(self, expression: exp.Whens) -> str:
    3827        return self.expressions(expression, sep=" ", indent=False)
    3828
    3829    def merge_sql(self, expression: exp.Merge) -> str:
    3830        table = expression.this
    3831        table_alias = ""
    3832
    3833        hints = table.args.get("hints")
    3834        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3835            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3836            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3837
    3838        this = self.sql(table)
    3839        using = f"USING {self.sql(expression, 'using')}"
    3840        on = f"ON {self.sql(expression, 'on')}"
    3841        whens = self.sql(expression, "whens")
    3842
    3843        returning = self.sql(expression, "returning")
    3844        if returning:
    3845            whens = f"{whens}{returning}"
    3846
    3847        sep = self.sep()
    3848
    3849        return self.prepend_ctes(
    3850            expression,
    3851            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    3852        )
    3853
    3854    @unsupported_args("format")
    3855    def tochar_sql(self, expression: exp.ToChar) -> str:
    3856        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
    3857
    3858    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3859        if not self.SUPPORTS_TO_NUMBER:
    3860            self.unsupported("Unsupported TO_NUMBER function")
    3861            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3862
    3863        fmt = expression.args.get("format")
    3864        if not fmt:
    3865            self.unsupported("Conversion format is required for TO_NUMBER")
    3866            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3867
    3868        return self.func("TO_NUMBER", expression.this, fmt)
    3869
    3870    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3871        this = self.sql(expression, "this")
    3872        kind = self.sql(expression, "kind")
    3873        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3874        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3875        return f"{this}({kind}{args})"
    3876
    3877    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3878        this = self.sql(expression, "this")
    3879        max = self.sql(expression, "max")
    3880        min = self.sql(expression, "min")
    3881        return f"{this}(MIN {min} MAX {max})"
    3882
    3883    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    3884        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    3885
    3886    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
    3887        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
    3888
    3889    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
    3890    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
    3891        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
    3892
    3893    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
    3894    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    3895        expressions = self.expressions(expression, flat=True)
    3896        expressions = f" {self.wrap(expressions)}" if expressions else ""
    3897        buckets = self.sql(expression, "buckets")
    3898        kind = self.sql(expression, "kind")
    3899        buckets = f" BUCKETS {buckets}" if buckets else ""
    3900        order = self.sql(expression, "order")
    3901        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
    3902
    3903    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    3904        return ""
    3905
    3906    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3907        expressions = self.expressions(expression, key="expressions", flat=True)
    3908        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3909        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3910        buckets = self.sql(expression, "buckets")
    3911        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
    3912
    3913    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3914        this = self.sql(expression, "this")
    3915        having = self.sql(expression, "having")
    3916
    3917        if having:
    3918            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3919
    3920        return self.func("ANY_VALUE", this)
    3921
    3922    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3923        transform = self.func("TRANSFORM", *expression.expressions)
    3924        row_format_before = self.sql(expression, "row_format_before")
    3925        row_format_before = f" {row_format_before}" if row_format_before else ""
    3926        record_writer = self.sql(expression, "record_writer")
    3927        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3928        using = f" USING {self.sql(expression, 'command_script')}"
    3929        schema = self.sql(expression, "schema")
    3930        schema = f" AS {schema}" if schema else ""
    3931        row_format_after = self.sql(expression, "row_format_after")
    3932        row_format_after = f" {row_format_after}" if row_format_after else ""
    3933        record_reader = self.sql(expression, "record_reader")
    3934        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3935        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
    3936
    3937    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3938        key_block_size = self.sql(expression, "key_block_size")
    3939        if key_block_size:
    3940            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3941
    3942        using = self.sql(expression, "using")
    3943        if using:
    3944            return f"USING {using}"
    3945
    3946        parser = self.sql(expression, "parser")
    3947        if parser:
    3948            return f"WITH PARSER {parser}"
    3949
    3950        comment = self.sql(expression, "comment")
    3951        if comment:
    3952            return f"COMMENT {comment}"
    3953
    3954        visible = expression.args.get("visible")
    3955        if visible is not None:
    3956            return "VISIBLE" if visible else "INVISIBLE"
    3957
    3958        engine_attr = self.sql(expression, "engine_attr")
    3959        if engine_attr:
    3960            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3961
    3962        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3963        if secondary_engine_attr:
    3964            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3965
    3966        self.unsupported("Unsupported index constraint option.")
    3967        return ""
    3968
    3969    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
    3970        enforced = " ENFORCED" if expression.args.get("enforced") else ""
    3971        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
    3972
    3973    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3974        kind = self.sql(expression, "kind")
    3975        kind = f"{kind} INDEX" if kind else "INDEX"
    3976        this = self.sql(expression, "this")
    3977        this = f" {this}" if this else ""
    3978        index_type = self.sql(expression, "index_type")
    3979        index_type = f" USING {index_type}" if index_type else ""
    3980        expressions = self.expressions(expression, flat=True)
    3981        expressions = f" ({expressions})" if expressions else ""
    3982        options = self.expressions(expression, key="options", sep=" ")
    3983        options = f" {options}" if options else ""
    3984        return f"{kind}{this}{index_type}{expressions}{options}"
    3985
    3986    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3987        if self.NVL2_SUPPORTED:
    3988            return self.function_fallback_sql(expression)
    3989
    3990        case = exp.Case().when(
    3991            expression.this.is_(exp.null()).not_(copy=False),
    3992            expression.args["true"],
    3993            copy=False,
    3994        )
    3995        else_cond = expression.args.get("false")
    3996        if else_cond:
    3997            case.else_(else_cond, copy=False)
    3998
    3999        return self.sql(case)
    4000
    4001    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    4002        this = self.sql(expression, "this")
    4003        expr = self.sql(expression, "expression")
    4004        iterator = self.sql(expression, "iterator")
    4005        condition = self.sql(expression, "condition")
    4006        condition = f" IF {condition}" if condition else ""
    4007        return f"{this} FOR {expr} IN {iterator}{condition}"
    4008
    4009    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    4010        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
    4011
    4012    def opclass_sql(self, expression: exp.Opclass) -> str:
    4013        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    4014
    4015    def predict_sql(self, expression: exp.Predict) -> str:
    4016        model = self.sql(expression, "this")
    4017        model = f"MODEL {model}"
    4018        table = self.sql(expression, "expression")
    4019        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    4020        parameters = self.sql(expression, "params_struct")
    4021        return self.func("PREDICT", model, table, parameters or None)
    4022
    4023    def forin_sql(self, expression: exp.ForIn) -> str:
    4024        this = self.sql(expression, "this")
    4025        expression_sql = self.sql(expression, "expression")
    4026        return f"FOR {this} DO {expression_sql}"
    4027
    4028    def refresh_sql(self, expression: exp.Refresh) -> str:
    4029        this = self.sql(expression, "this")
    4030        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    4031        return f"REFRESH {table}{this}"
    4032
    4033    def toarray_sql(self, expression: exp.ToArray) -> str:
    4034        arg = expression.this
    4035        if not arg.type:
    4036            from sqlglot.optimizer.annotate_types import annotate_types
    4037
    4038            arg = annotate_types(arg, dialect=self.dialect)
    4039
    4040        if arg.is_type(exp.DataType.Type.ARRAY):
    4041            return self.sql(arg)
    4042
    4043        cond_for_null = arg.is_(exp.null())
    4044        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
    4045
    4046    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    4047        this = expression.this
    4048        time_format = self.format_time(expression)
    4049
    4050        if time_format:
    4051            return self.sql(
    4052                exp.cast(
    4053                    exp.StrToTime(this=this, format=expression.args["format"]),
    4054                    exp.DataType.Type.TIME,
    4055                )
    4056            )
    4057
    4058        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    4059            return self.sql(this)
    4060
    4061        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
    4062
    4063    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    4064        this = expression.this
    4065        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    4066            return self.sql(this)
    4067
    4068        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
    4069
    4070    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    4071        this = expression.this
    4072        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    4073            return self.sql(this)
    4074
    4075        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
    4076
    4077    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    4078        this = expression.this
    4079        time_format = self.format_time(expression)
    4080
    4081        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    4082            return self.sql(
    4083                exp.cast(
    4084                    exp.StrToTime(this=this, format=expression.args["format"]),
    4085                    exp.DataType.Type.DATE,
    4086                )
    4087            )
    4088
    4089        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    4090            return self.sql(this)
    4091
    4092        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
    4093
    4094    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    4095        return self.sql(
    4096            exp.func(
    4097                "DATEDIFF",
    4098                expression.this,
    4099                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    4100                "day",
    4101            )
    4102        )
    4103
    4104    def lastday_sql(self, expression: exp.LastDay) -> str:
    4105        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4106            return self.function_fallback_sql(expression)
    4107
    4108        unit = expression.text("unit")
    4109        if unit and unit != "MONTH":
    4110            self.unsupported("Date parts are not supported in LAST_DAY.")
    4111
    4112        return self.func("LAST_DAY", expression.this)
    4113
    4114    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4115        from sqlglot.dialects.dialect import unit_to_str
    4116
    4117        return self.func(
    4118            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4119        )
    4120
    4121    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4122        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4123            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4124            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4125            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4126            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4127
    4128        from sqlglot.dialects import Dialect
    4129
    4130        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4131        if self.dialect.__class__ != Dialect:
    4132            self.unsupported("ARRAY_ANY is unsupported")
    4133
    4134        return self.function_fallback_sql(expression)
    4135
    4136    def struct_sql(self, expression: exp.Struct) -> str:
    4137        expression.set(
    4138            "expressions",
    4139            [
    4140                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4141                if isinstance(e, exp.PropertyEQ)
    4142                else e
    4143                for e in expression.expressions
    4144            ],
    4145        )
    4146
    4147        return self.function_fallback_sql(expression)
    4148
    4149    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4150        low = self.sql(expression, "this")
    4151        high = self.sql(expression, "expression")
    4152
    4153        return f"{low} TO {high}"
    4154
    4155    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4156        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4157        tables = f" {self.expressions(expression)}"
    4158
    4159        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4160
    4161        on_cluster = self.sql(expression, "cluster")
    4162        on_cluster = f" {on_cluster}" if on_cluster else ""
    4163
    4164        identity = self.sql(expression, "identity")
    4165        identity = f" {identity} IDENTITY" if identity else ""
    4166
    4167        option = self.sql(expression, "option")
    4168        option = f" {option}" if option else ""
    4169
    4170        partition = self.sql(expression, "partition")
    4171        partition = f" {partition}" if partition else ""
    4172
    4173        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
    4174
    4175    # This transpiles T-SQL's CONVERT function
    4176    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
    4177    def convert_sql(self, expression: exp.Convert) -> str:
    4178        to = expression.this
    4179        value = expression.expression
    4180        style = expression.args.get("style")
    4181        safe = expression.args.get("safe")
    4182        strict = expression.args.get("strict")
    4183
    4184        if not to or not value:
    4185            return ""
    4186
    4187        # Retrieve length of datatype and override to default if not specified
    4188        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4189            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4190
    4191        transformed: t.Optional[exp.Expression] = None
    4192        cast = exp.Cast if strict else exp.TryCast
    4193
    4194        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4195        if isinstance(style, exp.Literal) and style.is_int:
    4196            from sqlglot.dialects.tsql import TSQL
    4197
    4198            style_value = style.name
    4199            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4200            if not converted_style:
    4201                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4202
    4203            fmt = exp.Literal.string(converted_style)
    4204
    4205            if to.this == exp.DataType.Type.DATE:
    4206                transformed = exp.StrToDate(this=value, format=fmt)
    4207            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4208                transformed = exp.StrToTime(this=value, format=fmt)
    4209            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4210                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4211            elif to.this == exp.DataType.Type.TEXT:
    4212                transformed = exp.TimeToStr(this=value, format=fmt)
    4213
    4214        if not transformed:
    4215            transformed = cast(this=value, to=to, safe=safe)
    4216
    4217        return self.sql(transformed)
    4218
    4219    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
    4220        this = expression.this
    4221        if isinstance(this, exp.JSONPathWildcard):
    4222            this = self.json_path_part(this)
    4223            return f".{this}" if this else ""
    4224
    4225        if exp.SAFE_IDENTIFIER_RE.match(this):
    4226            return f".{this}"
    4227
    4228        this = self.json_path_part(this)
    4229        return (
    4230            f"[{this}]"
    4231            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
    4232            else f".{this}"
    4233        )
    4234
    4235    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
    4236        this = self.json_path_part(expression.this)
    4237        return f"[{this}]" if this else ""
    4238
    4239    def _simplify_unless_literal(self, expression: E) -> E:
    4240        if not isinstance(expression, exp.Literal):
    4241            from sqlglot.optimizer.simplify import simplify
    4242
    4243            expression = simplify(expression, dialect=self.dialect)
    4244
    4245        return expression
    4246
    4247    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
    4248        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
    4249            # The first modifier here will be the one closest to the AggFunc's arg
    4250            mods = sorted(
    4251                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
    4252                key=lambda x: 0
    4253                if isinstance(x, exp.HavingMax)
    4254                else (1 if isinstance(x, exp.Order) else 2),
    4255            )
    4256
    4257            if mods:
    4258                mod = mods[0]
    4259                this = expression.__class__(this=mod.this.copy())
    4260                this.meta["inline"] = True
    4261                mod.this.replace(this)
    4262                return self.sql(expression.this)
    4263
    4264            agg_func = expression.find(exp.AggFunc)
    4265
    4266            if agg_func:
    4267                return self.sql(agg_func)[:-1] + f" {text})"
    4268
    4269        return f"{self.sql(expression, 'this')} {text}"
    4270
    4271    def _replace_line_breaks(self, string: str) -> str:
    4272        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
    4273        if self.pretty:
    4274            return string.replace("\n", self.SENTINEL_LINE_BREAK)
    4275        return string
    4276
    4277    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4278        option = self.sql(expression, "this")
    4279
    4280        if expression.expressions:
    4281            upper = option.upper()
    4282
    4283            # Snowflake FILE_FORMAT options are separated by whitespace
    4284            sep = " " if upper == "FILE_FORMAT" else ", "
    4285
    4286            # Databricks copy/format options do not set their list of values with EQ
    4287            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4288            values = self.expressions(expression, flat=True, sep=sep)
    4289            return f"{option}{op}({values})"
    4290
    4291        value = self.sql(expression, "expression")
    4292
    4293        if not value:
    4294            return option
    4295
    4296        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4297
    4298        return f"{option}{op}{value}"
    4299
    4300    def credentials_sql(self, expression: exp.Credentials) -> str:
    4301        cred_expr = expression.args.get("credentials")
    4302        if isinstance(cred_expr, exp.Literal):
    4303            # Redshift case: CREDENTIALS <string>
    4304            credentials = self.sql(expression, "credentials")
    4305            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4306        else:
    4307            # Snowflake case: CREDENTIALS = (...)
    4308            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4309            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4310
    4311        storage = self.sql(expression, "storage")
    4312        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4313
    4314        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4315        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4316
    4317        iam_role = self.sql(expression, "iam_role")
    4318        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4319
    4320        region = self.sql(expression, "region")
    4321        region = f" REGION {region}" if region else ""
    4322
    4323        return f"{credentials}{storage}{encryption}{iam_role}{region}"
    4324
    4325    def copy_sql(self, expression: exp.Copy) -> str:
    4326        this = self.sql(expression, "this")
    4327        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4328
    4329        credentials = self.sql(expression, "credentials")
    4330        credentials = self.seg(credentials) if credentials else ""
    4331        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    4332        files = self.expressions(expression, key="files", flat=True)
    4333
    4334        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4335        params = self.expressions(
    4336            expression,
    4337            key="params",
    4338            sep=sep,
    4339            new_line=True,
    4340            skip_last=True,
    4341            skip_first=True,
    4342            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4343        )
    4344
    4345        if params:
    4346            if self.COPY_PARAMS_ARE_WRAPPED:
    4347                params = f" WITH ({params})"
    4348            elif not self.pretty:
    4349                params = f" {params}"
    4350
    4351        return f"COPY{this}{kind} {files}{credentials}{params}"
    4352
    4353    def semicolon_sql(self, expression: exp.Semicolon) -> str:
    4354        return ""
    4355
    4356    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4357        on_sql = "ON" if expression.args.get("on") else "OFF"
    4358        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4359        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4360        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4361        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4362
    4363        if filter_col or retention_period:
    4364            on_sql = self.func("ON", filter_col, retention_period)
    4365
    4366        return f"DATA_DELETION={on_sql}"
    4367
    4368    def maskingpolicycolumnconstraint_sql(
    4369        self, expression: exp.MaskingPolicyColumnConstraint
    4370    ) -> str:
    4371        this = self.sql(expression, "this")
    4372        expressions = self.expressions(expression, flat=True)
    4373        expressions = f" USING ({expressions})" if expressions else ""
    4374        return f"MASKING POLICY {this}{expressions}"
    4375
    4376    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4377        this = self.sql(expression, "this")
    4378        this = f"TABLE {this}"
    4379        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
    4380
    4381    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    4382        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
    4383
    4384    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4385        this = self.sql(expression, "this")
    4386        expr = expression.expression
    4387
    4388        if isinstance(expr, exp.Func):
    4389            # T-SQL's CLR functions are case sensitive
    4390            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4391        else:
    4392            expr = self.sql(expression, "expression")
    4393
    4394        return self.scope_resolution(expr, this)
    4395
    4396    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4397        if self.PARSE_JSON_NAME is None:
    4398            return self.sql(expression.this)
    4399
    4400        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
    4401
    4402    def rand_sql(self, expression: exp.Rand) -> str:
    4403        lower = self.sql(expression, "lower")
    4404        upper = self.sql(expression, "upper")
    4405
    4406        if lower and upper:
    4407            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4408        return self.func("RAND", expression.this)
    4409
    4410    def changes_sql(self, expression: exp.Changes) -> str:
    4411        information = self.sql(expression, "information")
    4412        information = f"INFORMATION => {information}"
    4413        at_before = self.sql(expression, "at_before")
    4414        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4415        end = self.sql(expression, "end")
    4416        end = f"{self.seg('')}{end}" if end else ""
    4417
    4418        return f"CHANGES ({information}){at_before}{end}"
    4419
    4420    def pad_sql(self, expression: exp.Pad) -> str:
    4421        prefix = "L" if expression.args.get("is_left") else "R"
    4422
    4423        fill_pattern = self.sql(expression, "fill_pattern") or None
    4424        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4425            fill_pattern = "' '"
    4426
    4427        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
    4428
    4429    def summarize_sql(self, expression: exp.Summarize) -> str:
    4430        table = " TABLE" if expression.args.get("table") else ""
    4431        return f"SUMMARIZE{table} {self.sql(expression.this)}"
    4432
    4433    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4434        generate_series = exp.GenerateSeries(**expression.args)
    4435
    4436        parent = expression.parent
    4437        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4438            parent = parent.parent
    4439
    4440        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4441            return self.sql(exp.Unnest(expressions=[generate_series]))
    4442
    4443        if isinstance(parent, exp.Select):
    4444            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4445
    4446        return self.sql(generate_series)
    4447
    4448    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4449        exprs = expression.expressions
    4450        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4451            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4452        else:
    4453            rhs = self.expressions(expression)
    4454
    4455        return self.func(name, expression.this, rhs or None)
    4456
    4457    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4458        if self.SUPPORTS_CONVERT_TIMEZONE:
    4459            return self.function_fallback_sql(expression)
    4460
    4461        source_tz = expression.args.get("source_tz")
    4462        target_tz = expression.args.get("target_tz")
    4463        timestamp = expression.args.get("timestamp")
    4464
    4465        if source_tz and timestamp:
    4466            timestamp = exp.AtTimeZone(
    4467                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4468            )
    4469
    4470        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4471
    4472        return self.sql(expr)
    4473
    4474    def json_sql(self, expression: exp.JSON) -> str:
    4475        this = self.sql(expression, "this")
    4476        this = f" {this}" if this else ""
    4477
    4478        _with = expression.args.get("with")
    4479
    4480        if _with is None:
    4481            with_sql = ""
    4482        elif not _with:
    4483            with_sql = " WITHOUT"
    4484        else:
    4485            with_sql = " WITH"
    4486
    4487        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4488
    4489        return f"JSON{this}{with_sql}{unique_sql}"
    4490
    4491    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4492        def _generate_on_options(arg: t.Any) -> str:
    4493            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4494
    4495        path = self.sql(expression, "path")
    4496        returning = self.sql(expression, "returning")
    4497        returning = f" RETURNING {returning}" if returning else ""
    4498
    4499        on_condition = self.sql(expression, "on_condition")
    4500        on_condition = f" {on_condition}" if on_condition else ""
    4501
    4502        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
    4503
    4504    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4505        else_ = "ELSE " if expression.args.get("else_") else ""
    4506        condition = self.sql(expression, "expression")
    4507        condition = f"WHEN {condition} THEN " if condition else else_
    4508        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4509        return f"{condition}{insert}"
    4510
    4511    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4512        kind = self.sql(expression, "kind")
    4513        expressions = self.seg(self.expressions(expression, sep=" "))
    4514        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4515        return res
    4516
    4517    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4518        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4519        empty = expression.args.get("empty")
    4520        empty = (
    4521            f"DEFAULT {empty} ON EMPTY"
    4522            if isinstance(empty, exp.Expression)
    4523            else self.sql(expression, "empty")
    4524        )
    4525
    4526        error = expression.args.get("error")
    4527        error = (
    4528            f"DEFAULT {error} ON ERROR"
    4529            if isinstance(error, exp.Expression)
    4530            else self.sql(expression, "error")
    4531        )
    4532
    4533        if error and empty:
    4534            error = (
    4535                f"{empty} {error}"
    4536                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4537                else f"{error} {empty}"
    4538            )
    4539            empty = ""
    4540
    4541        null = self.sql(expression, "null")
    4542
    4543        return f"{empty}{error}{null}"
    4544
    4545    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
    4546        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
    4547        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
    4548
    4549    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4550        this = self.sql(expression, "this")
    4551        path = self.sql(expression, "path")
    4552
    4553        passing = self.expressions(expression, "passing")
    4554        passing = f" PASSING {passing}" if passing else ""
    4555
    4556        on_condition = self.sql(expression, "on_condition")
    4557        on_condition = f" {on_condition}" if on_condition else ""
    4558
    4559        path = f"{path}{passing}{on_condition}"
    4560
    4561        return self.func("JSON_EXISTS", this, path)
    4562
    4563    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4564        array_agg = self.function_fallback_sql(expression)
    4565
    4566        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4567        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4568        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4569            parent = expression.parent
    4570            if isinstance(parent, exp.Filter):
    4571                parent_cond = parent.expression.this
    4572                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4573            else:
    4574                this = expression.this
    4575                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4576                if this.find(exp.Column):
    4577                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4578                    this_sql = (
    4579                        self.expressions(this)
    4580                        if isinstance(this, exp.Distinct)
    4581                        else self.sql(expression, "this")
    4582                    )
    4583
    4584                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4585
    4586        return array_agg
    4587
    4588    def apply_sql(self, expression: exp.Apply) -> str:
    4589        this = self.sql(expression, "this")
    4590        expr = self.sql(expression, "expression")
    4591
    4592        return f"{this} APPLY({expr})"
    4593
    4594    def grant_sql(self, expression: exp.Grant) -> str:
    4595        privileges_sql = self.expressions(expression, key="privileges", flat=True)
    4596
    4597        kind = self.sql(expression, "kind")
    4598        kind = f" {kind}" if kind else ""
    4599
    4600        securable = self.sql(expression, "securable")
    4601        securable = f" {securable}" if securable else ""
    4602
    4603        principals = self.expressions(expression, key="principals", flat=True)
    4604
    4605        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
    4606
    4607        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
    4608
    4609    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4610        this = self.sql(expression, "this")
    4611        columns = self.expressions(expression, flat=True)
    4612        columns = f"({columns})" if columns else ""
    4613
    4614        return f"{this}{columns}"
    4615
    4616    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4617        this = self.sql(expression, "this")
    4618
    4619        kind = self.sql(expression, "kind")
    4620        kind = f"{kind} " if kind else ""
    4621
    4622        return f"{kind}{this}"
    4623
    4624    def columns_sql(self, expression: exp.Columns):
    4625        func = self.function_fallback_sql(expression)
    4626        if expression.args.get("unpack"):
    4627            func = f"*{func}"
    4628
    4629        return func
    4630
    4631    def overlay_sql(self, expression: exp.Overlay):
    4632        this = self.sql(expression, "this")
    4633        expr = self.sql(expression, "expression")
    4634        from_sql = self.sql(expression, "from")
    4635        for_sql = self.sql(expression, "for")
    4636        for_sql = f" FOR {for_sql}" if for_sql else ""
    4637
    4638        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
    4639
    4640    @unsupported_args("format")
    4641    def todouble_sql(self, expression: exp.ToDouble) -> str:
    4642        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    4643
    4644    def string_sql(self, expression: exp.String) -> str:
    4645        this = expression.this
    4646        zone = expression.args.get("zone")
    4647
    4648        if zone:
    4649            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4650            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4651            # set for source_tz to transpile the time conversion before the STRING cast
    4652            this = exp.ConvertTimezone(
    4653                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4654            )
    4655
    4656        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
    4657
    4658    def median_sql(self, expression: exp.Median):
    4659        if not self.SUPPORTS_MEDIAN:
    4660            return self.sql(
    4661                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4662            )
    4663
    4664        return self.function_fallback_sql(expression)
    4665
    4666    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4667        filler = self.sql(expression, "this")
    4668        filler = f" {filler}" if filler else ""
    4669        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4670        return f"TRUNCATE{filler} {with_count}"
    4671
    4672    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4673        if self.SUPPORTS_UNIX_SECONDS:
    4674            return self.function_fallback_sql(expression)
    4675
    4676        start_ts = exp.cast(
    4677            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4678        )
    4679
    4680        return self.sql(
    4681            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    4682        )
    4683
    4684    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    4685        dim = expression.expression
    4686
    4687        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    4688        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    4689            if not (dim.is_int and dim.name == "1"):
    4690                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    4691            dim = None
    4692
    4693        # If dimension is required but not specified, default initialize it
    4694        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    4695            dim = exp.Literal.number(1)
    4696
    4697        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
    4698
    4699    def attach_sql(self, expression: exp.Attach) -> str:
    4700        this = self.sql(expression, "this")
    4701        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    4702        expressions = self.expressions(expression)
    4703        expressions = f" ({expressions})" if expressions else ""
    4704
    4705        return f"ATTACH{exists_sql} {this}{expressions}"
    4706
    4707    def detach_sql(self, expression: exp.Detach) -> str:
    4708        this = self.sql(expression, "this")
    4709        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    4710
    4711        return f"DETACH{exists_sql} {this}"
    4712
    4713    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    4714        this = self.sql(expression, "this")
    4715        value = self.sql(expression, "expression")
    4716        value = f" {value}" if value else ""
    4717        return f"{this}{value}"
    4718
    4719    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4720        this_sql = self.sql(expression, "this")
    4721        if isinstance(expression.this, exp.Table):
    4722            this_sql = f"TABLE {this_sql}"
    4723
    4724        return self.func(
    4725            "FEATURES_AT_TIME",
    4726            this_sql,
    4727            expression.args.get("time"),
    4728            expression.args.get("num_rows"),
    4729            expression.args.get("ignore_feature_nulls"),
    4730        )
    4731
    4732    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    4733        return (
    4734            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    4735        )
    4736
    4737    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    4738        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    4739        encode = f"{encode} {self.sql(expression, 'this')}"
    4740
    4741        properties = expression.args.get("properties")
    4742        if properties:
    4743            encode = f"{encode} {self.properties(properties)}"
    4744
    4745        return encode
    4746
    4747    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    4748        this = self.sql(expression, "this")
    4749        include = f"INCLUDE {this}"
    4750
    4751        column_def = self.sql(expression, "column_def")
    4752        if column_def:
    4753            include = f"{include} {column_def}"
    4754
    4755        alias = self.sql(expression, "alias")
    4756        if alias:
    4757            include = f"{include} AS {alias}"
    4758
    4759        return include
    4760
    4761    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
    4762        name = f"NAME {self.sql(expression, 'this')}"
    4763        return self.func("XMLELEMENT", name, *expression.expressions)
    4764
    4765    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    4766        partitions = self.expressions(expression, "partition_expressions")
    4767        create = self.expressions(expression, "create_expressions")
    4768        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
    4769
    4770    def partitionbyrangepropertydynamic_sql(
    4771        self, expression: exp.PartitionByRangePropertyDynamic
    4772    ) -> str:
    4773        start = self.sql(expression, "start")
    4774        end = self.sql(expression, "end")
    4775
    4776        every = expression.args["every"]
    4777        if isinstance(every, exp.Interval) and every.this.is_string:
    4778            every.this.replace(exp.Literal.number(every.name))
    4779
    4780        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
    4781
    4782    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    4783        name = self.sql(expression, "this")
    4784        values = self.expressions(expression, flat=True)
    4785
    4786        return f"NAME {name} VALUE {values}"
    4787
    4788    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    4789        kind = self.sql(expression, "kind")
    4790        sample = self.sql(expression, "sample")
    4791        return f"SAMPLE {sample} {kind}"
    4792
    4793    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    4794        kind = self.sql(expression, "kind")
    4795        option = self.sql(expression, "option")
    4796        option = f" {option}" if option else ""
    4797        this = self.sql(expression, "this")
    4798        this = f" {this}" if this else ""
    4799        columns = self.expressions(expression)
    4800        columns = f" {columns}" if columns else ""
    4801        return f"{kind}{option} STATISTICS{this}{columns}"
    4802
    4803    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    4804        this = self.sql(expression, "this")
    4805        columns = self.expressions(expression)
    4806        inner_expression = self.sql(expression, "expression")
    4807        inner_expression = f" {inner_expression}" if inner_expression else ""
    4808        update_options = self.sql(expression, "update_options")
    4809        update_options = f" {update_options} UPDATE" if update_options else ""
    4810        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
    4811
    4812    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    4813        kind = self.sql(expression, "kind")
    4814        kind = f" {kind}" if kind else ""
    4815        return f"DELETE{kind} STATISTICS"
    4816
    4817    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
    4818        inner_expression = self.sql(expression, "expression")
    4819        return f"LIST CHAINED ROWS{inner_expression}"
    4820
    4821    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    4822        kind = self.sql(expression, "kind")
    4823        this = self.sql(expression, "this")
    4824        this = f" {this}" if this else ""
    4825        inner_expression = self.sql(expression, "expression")
    4826        return f"VALIDATE {kind}{this}{inner_expression}"
    4827
    4828    def analyze_sql(self, expression: exp.Analyze) -> str:
    4829        options = self.expressions(expression, key="options", sep=" ")
    4830        options = f" {options}" if options else ""
    4831        kind = self.sql(expression, "kind")
    4832        kind = f" {kind}" if kind else ""
    4833        this = self.sql(expression, "this")
    4834        this = f" {this}" if this else ""
    4835        mode = self.sql(expression, "mode")
    4836        mode = f" {mode}" if mode else ""
    4837        properties = self.sql(expression, "properties")
    4838        properties = f" {properties}" if properties else ""
    4839        partition = self.sql(expression, "partition")
    4840        partition = f" {partition}" if partition else ""
    4841        inner_expression = self.sql(expression, "expression")
    4842        inner_expression = f" {inner_expression}" if inner_expression else ""
    4843        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
    4844
    4845    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    4846        this = self.sql(expression, "this")
    4847        namespaces = self.expressions(expression, key="namespaces")
    4848        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    4849        passing = self.expressions(expression, key="passing")
    4850        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    4851        columns = self.expressions(expression, key="columns")
    4852        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    4853        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    4854        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
    4855
    4856    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
    4857        this = self.sql(expression, "this")
    4858        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
    4859
    4860    def export_sql(self, expression: exp.Export) -> str:
    4861        this = self.sql(expression, "this")
    4862        connection = self.sql(expression, "connection")
    4863        connection = f"WITH CONNECTION {connection} " if connection else ""
    4864        options = self.sql(expression, "options")
    4865        return f"EXPORT DATA {connection}{options} AS {this}"
    4866
    4867    def declare_sql(self, expression: exp.Declare) -> str:
    4868        return f"DECLARE {self.expressions(expression, flat=True)}"
    4869
    4870    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
    4871        variable = self.sql(expression, "this")
    4872        default = self.sql(expression, "default")
    4873        default = f" = {default}" if default else ""
    4874
    4875        kind = self.sql(expression, "kind")
    4876        if isinstance(expression.args.get("kind"), exp.Schema):
    4877            kind = f"TABLE {kind}"
    4878
    4879        return f"{variable} AS {kind}{default}"
    4880
    4881    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
    4882        kind = self.sql(expression, "kind")
    4883        this = self.sql(expression, "this")
    4884        set = self.sql(expression, "expression")
    4885        using = self.sql(expression, "using")
    4886        using = f" USING {using}" if using else ""
    4887
    4888        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
    4889
    4890        return f"{kind_sql} {this} SET {set}{using}"
    4891
    4892    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
    4893        params = self.expressions(expression, key="params", flat=True)
    4894        return self.func(expression.name, *expression.expressions) + f"({params})"
    4895
    4896    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
    4897        return self.func(expression.name, *expression.expressions)
    4898
    4899    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
    4900        return self.anonymousaggfunc_sql(expression)
    4901
    4902    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
    4903        return self.parameterizedagg_sql(expression)
    4904
    4905    def show_sql(self, expression: exp.Show) -> str:
    4906        self.unsupported("Unsupported SHOW statement")
    4907        return ""
    4908
    4909    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
    4910        # Snowflake GET/PUT statements:
    4911        #   PUT <file> <internalStage> <properties>
    4912        #   GET <internalStage> <file> <properties>
    4913        props = expression.args.get("properties")
    4914        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
    4915        this = self.sql(expression, "this")
    4916        target = self.sql(expression, "target")
    4917
    4918        if isinstance(expression, exp.Put):
    4919            return f"PUT {this} {target}{props_sql}"
    4920        else:
    4921            return f"GET {target} {this}{props_sql}"

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 or 'always': Always quote. '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

Generator( pretty: Optional[bool] = None, identify: str | bool = False, normalize: bool = False, pad: int = 2, indent: int = 2, normalize_functions: Union[str, bool, NoneType] = None, unsupported_level: sqlglot.errors.ErrorLevel = <ErrorLevel.WARN: 'WARN'>, max_unsupported: int = 3, leading_comma: bool = False, max_text_width: int = 80, comments: bool = True, dialect: Union[str, sqlglot.dialects.Dialect, Type[sqlglot.dialects.Dialect], NoneType] = None) View Source

    687    def __init__(
    688        self,
    689        pretty: t.Optional[bool] = None,
    690        identify: str | bool = False,
    691        normalize: bool = False,
    692        pad: int = 2,
    693        indent: int = 2,
    694        normalize_functions: t.Optional[str | bool] = None,
    695        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    696        max_unsupported: int = 3,
    697        leading_comma: bool = False,
    698        max_text_width: int = 80,
    699        comments: bool = True,
    700        dialect: DialectType = None,
    701    ):
    702        import sqlglot
    703        from sqlglot.dialects import Dialect
    704
    705        self.pretty = pretty if pretty is not None else sqlglot.pretty
    706        self.identify = identify
    707        self.normalize = normalize
    708        self.pad = pad
    709        self._indent = indent
    710        self.unsupported_level = unsupported_level
    711        self.max_unsupported = max_unsupported
    712        self.leading_comma = leading_comma
    713        self.max_text_width = max_text_width
    714        self.comments = comments
    715        self.dialect = Dialect.get_or_raise(dialect)
    716
    717        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    718        self.normalize_functions = (
    719            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    720        )
    721
    722        self.unsupported_messages: t.List[str] = []
    723        self._escaped_quote_end: str = (
    724            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    725        )
    726        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
    727
    728        self._next_name = name_sequence("_t")
    729
    730        self._identifier_start = self.dialect.IDENTIFIER_START
    731        self._identifier_end = self.dialect.IDENTIFIER_END
    732
    733        self._quote_json_path_key_using_brackets = True

TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeColumns'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeWith'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayContainsAll'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayOverlaps'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CredentialsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EmptyProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Except'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Floor'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Get'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Intersect'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Int64'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PartitionedByBucket'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PartitionByTruncate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Put'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Stream'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StreamingTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SwapTable'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Tags'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Union'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingTemplateProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingData'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Uuid'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithProcedureOptions'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ForceProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SET_OP_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
SUPPORTS_UESCAPE = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
QUOTE_JSON_PATH = True
PAD_FILL_PATTERN_IS_REQUIRED = False
SUPPORTS_EXPLODING_PROJECTIONS = True
ARRAY_CONCAT_IS_VAR_LEN = True
SUPPORTS_CONVERT_TIMEZONE = False
SUPPORTS_MEDIAN = True
SUPPORTS_UNIX_SECONDS = False
PARSE_JSON_NAME: Optional[str] = 'PARSE_JSON'
ARRAY_SIZE_NAME: str = 'ARRAY_LENGTH'
ALTER_SET_TYPE = 'SET DATA TYPE'
ARRAY_SIZE_DIM_REQUIRED: Optional[bool] = None
TYPE_MAPPING = {<Type.DATETIME2: 'DATETIME2'>: 'TIMESTAMP', <Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.BLOB: 'BLOB'>: 'VARBINARY', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY', <Type.SMALLDATETIME: 'SMALLDATETIME'>: 'TIMESTAMP'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: Set[str] = set()
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistributedByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DuplicateKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DynamicProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EmptyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EncodeProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.IncludeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SecurityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StorageHandlerProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StreamingTableProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StrictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Tags'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.UsingTemplateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithProcedureOptions'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ForceProperty'>: <Location.POST_CREATE: 'POST_CREATE'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Command'>, <class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Describe'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.MultitableInserts'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.SetOperation'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.NVARCHAR: 'NVARCHAR'>, <Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.VARCHAR: 'VARCHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str: View Source

    735    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
    736        """
    737        Generates the SQL string corresponding to the given syntax tree.
    738
    739        Args:
    740            expression: The syntax tree.
    741            copy: Whether to copy the expression. The generator performs mutations so
    742                it is safer to copy.
    743
    744        Returns:
    745            The SQL string corresponding to `expression`.
    746        """
    747        if copy:
    748            expression = expression.copy()
    749
    750        expression = self.preprocess(expression)
    751
    752        self.unsupported_messages = []
    753        sql = self.sql(expression).strip()
    754
    755        if self.pretty:
    756            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    757
    758        if self.unsupported_level == ErrorLevel.IGNORE:
    759            return sql
    760
    761        if self.unsupported_level == ErrorLevel.WARN:
    762            for msg in self.unsupported_messages:
    763                logger.warning(msg)
    764        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    765            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    766
    767        return sql

Generates the SQL string corresponding to the given syntax tree.

###### Arguments:

- expression: The syntax tree.
- copy: Whether to copy the expression. The generator performs mutations so it is safer to copy.

###### Returns:

> The SQL string corresponding to `expression`.

def preprocess( self, expression: sqlglot.expressions.Expression) -> sqlglot.expressions.Expression: View Source

    769    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    770        """Apply generic preprocessing transformations to a given expression."""
    771        expression = self._move_ctes_to_top_level(expression)
    772
    773        if self.ENSURE_BOOLS:
    774            from sqlglot.transforms import ensure_bools
    775
    776            expression = ensure_bools(expression)
    777
    778        return expression

Apply generic preprocessing transformations to a given expression.

def unsupported(self, message: str) -> None: View Source

    791    def unsupported(self, message: str) -> None:
    792        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    793            raise UnsupportedError(message)
    794        self.unsupported_messages.append(message)

def sep(self, sep: str = ' ') -> str: View Source

    796    def sep(self, sep: str = " ") -> str:
    797        return f"{sep.strip()}\n" if self.pretty else sep

def seg(self, sql: str, sep: str = ' ') -> str: View Source

    799    def seg(self, sql: str, sep: str = " ") -> str:
    800        return f"{self.sep(sep)}{sql}"

def pad_comment(self, comment: str) -> str: View Source

    802    def pad_comment(self, comment: str) -> str:
    803        comment = " " + comment if comment[0].strip() else comment
    804        comment = comment + " " if comment[-1].strip() else comment
    805        return comment

def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None, separated: bool = False) -> str: View Source

    807    def maybe_comment(
    808        self,
    809        sql: str,
    810        expression: t.Optional[exp.Expression] = None,
    811        comments: t.Optional[t.List[str]] = None,
    812        separated: bool = False,
    813    ) -> str:
    814        comments = (
    815            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    816            if self.comments
    817            else None
    818        )
    819
    820        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    821            return sql
    822
    823        comments_sql = " ".join(
    824            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    825        )
    826
    827        if not comments_sql:
    828            return sql
    829
    830        comments_sql = self._replace_line_breaks(comments_sql)
    831
    832        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    833            return (
    834                f"{self.sep()}{comments_sql}{sql}"
    835                if not sql or sql[0].isspace()
    836                else f"{comments_sql}{self.sep()}{sql}"
    837            )
    838
    839        return f"{sql} {comments_sql}"

def wrap(self, expression: sqlglot.expressions.Expression | str) -> str: View Source

    841    def wrap(self, expression: exp.Expression | str) -> str:
    842        this_sql = (
    843            self.sql(expression)
    844            if isinstance(expression, exp.UNWRAPPED_QUERIES)
    845            else self.sql(expression, "this")
    846        )
    847        if not this_sql:
    848            return "()"
    849
    850        this_sql = self.indent(this_sql, level=1, pad=0)
    851        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str: View Source

    853    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    854        original = self.identify
    855        self.identify = False
    856        result = func(*args, **kwargs)
    857        self.identify = original
    858        return result

def normalize_func(self, name: str) -> str: View Source

    860    def normalize_func(self, name: str) -> str:
    861        if self.normalize_functions == "upper" or self.normalize_functions is True:
    862            return name.upper()
    863        if self.normalize_functions == "lower":
    864            return name.lower()
    865        return name

def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str: View Source

    867    def indent(
    868        self,
    869        sql: str,
    870        level: int = 0,
    871        pad: t.Optional[int] = None,
    872        skip_first: bool = False,
    873        skip_last: bool = False,
    874    ) -> str:
    875        if not self.pretty or not sql:
    876            return sql
    877
    878        pad = self.pad if pad is None else pad
    879        lines = sql.split("\n")
    880
    881        return "\n".join(
    882            (
    883                line
    884                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    885                else f"{' ' * (level * self._indent + pad)}{line}"
    886            )
    887            for i, line in enumerate(lines)
    888        )

def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str: View Source

    890    def sql(
    891        self,
    892        expression: t.Optional[str | exp.Expression],
    893        key: t.Optional[str] = None,
    894        comment: bool = True,
    895    ) -> str:
    896        if not expression:
    897            return ""
    898
    899        if isinstance(expression, str):
    900            return expression
    901
    902        if key:
    903            value = expression.args.get(key)
    904            if value:
    905                return self.sql(value)
    906            return ""
    907
    908        transform = self.TRANSFORMS.get(expression.__class__)
    909
    910        if callable(transform):
    911            sql = transform(self, expression)
    912        elif isinstance(expression, exp.Expression):
    913            exp_handler_name = f"{expression.key}_sql"
    914
    915            if hasattr(self, exp_handler_name):
    916                sql = getattr(self, exp_handler_name)(expression)
    917            elif isinstance(expression, exp.Func):
    918                sql = self.function_fallback_sql(expression)
    919            elif isinstance(expression, exp.Property):
    920                sql = self.property_sql(expression)
    921            else:
    922                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    923        else:
    924            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    925
    926        return self.maybe_comment(sql, expression) if self.comments and comment else sql

def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str: View Source

    928    def uncache_sql(self, expression: exp.Uncache) -> str:
    929        table = self.sql(expression, "this")
    930        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    931        return f"UNCACHE TABLE{exists_sql} {table}"

def cache_sql(self, expression: sqlglot.expressions.Cache) -> str: View Source

    933    def cache_sql(self, expression: exp.Cache) -> str:
    934        lazy = " LAZY" if expression.args.get("lazy") else ""
    935        table = self.sql(expression, "this")
    936        options = expression.args.get("options")
    937        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    938        sql = self.sql(expression, "expression")
    939        sql = f" AS{self.sep()}{sql}" if sql else ""
    940        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    941        return self.prepend_ctes(expression, sql)

def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str: View Source

    943    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    944        if isinstance(expression.parent, exp.Cast):
    945            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    946        default = "DEFAULT " if expression.args.get("default") else ""
    947        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

def column_parts(self, expression: sqlglot.expressions.Column) -> str: View Source

    949    def column_parts(self, expression: exp.Column) -> str:
    950        return ".".join(
    951            self.sql(part)
    952            for part in (
    953                expression.args.get("catalog"),
    954                expression.args.get("db"),
    955                expression.args.get("table"),
    956                expression.args.get("this"),
    957            )
    958            if part
    959        )

def column_sql(self, expression: sqlglot.expressions.Column) -> str: View Source

    961    def column_sql(self, expression: exp.Column) -> str:
    962        join_mark = " (+)" if expression.args.get("join_mark") else ""
    963
    964        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
    965            join_mark = ""
    966            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    967
    968        return f"{self.column_parts(expression)}{join_mark}"

def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str: View Source

    970    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    971        this = self.sql(expression, "this")
    972        this = f" {this}" if this else ""
    973        position = self.sql(expression, "position")
    974        return f"{position}{this}"

def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str: View Source

    976    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    977        column = self.sql(expression, "this")
    978        kind = self.sql(expression, "kind")
    979        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    980        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    981        kind = f"{sep}{kind}" if kind else ""
    982        constraints = f" {constraints}" if constraints else ""
    983        position = self.sql(expression, "position")
    984        position = f" {position}" if position else ""
    985
    986        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    987            kind = ""
    988
    989        return f"{exists}{column}{kind}{constraints}{position}"

def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str: View Source

    991    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    992        this = self.sql(expression, "this")
    993        kind_sql = self.sql(expression, "kind").strip()
    994        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str: View Source

     996    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     997        this = self.sql(expression, "this")
     998        if expression.args.get("not_null"):
     999            persisted = " PERSISTED NOT NULL"
    1000        elif expression.args.get("persisted"):
    1001            persisted = " PERSISTED"
    1002        else:
    1003            persisted = ""
    1004        return f"AS {this}{persisted}"

def autoincrementcolumnconstraint_sql(self, _) -> str: View Source

    1006    def autoincrementcolumnconstraint_sql(self, _) -> str:
    1007        return self.token_sql(TokenType.AUTO_INCREMENT)

def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str: View Source

    1009    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    1010        if isinstance(expression.this, list):
    1011            this = self.wrap(self.expressions(expression, key="this", flat=True))
    1012        else:
    1013            this = self.sql(expression, "this")
    1014
    1015        return f"COMPRESS {this}"

def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str: View Source

    1017    def generatedasidentitycolumnconstraint_sql(
    1018        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1019    ) -> str:
    1020        this = ""
    1021        if expression.this is not None:
    1022            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1023            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1024
    1025        start = expression.args.get("start")
    1026        start = f"START WITH {start}" if start else ""
    1027        increment = expression.args.get("increment")
    1028        increment = f" INCREMENT BY {increment}" if increment else ""
    1029        minvalue = expression.args.get("minvalue")
    1030        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1031        maxvalue = expression.args.get("maxvalue")
    1032        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1033        cycle = expression.args.get("cycle")
    1034        cycle_sql = ""
    1035
    1036        if cycle is not None:
    1037            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1038            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1039
    1040        sequence_opts = ""
    1041        if start or increment or cycle_sql:
    1042            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1043            sequence_opts = f" ({sequence_opts.strip()})"
    1044
    1045        expr = self.sql(expression, "expression")
    1046        expr = f"({expr})" if expr else "IDENTITY"
    1047
    1048        return f"GENERATED{this} AS {expr}{sequence_opts}"

def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str: View Source

    1050    def generatedasrowcolumnconstraint_sql(
    1051        self, expression: exp.GeneratedAsRowColumnConstraint
    1052    ) -> str:
    1053        start = "START" if expression.args.get("start") else "END"
    1054        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1055        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str: View Source

    1057    def periodforsystemtimeconstraint_sql(
    1058        self, expression: exp.PeriodForSystemTimeConstraint
    1059    ) -> str:
    1060        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"

def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str: View Source

    1062    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
    1063        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"

def transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str: View Source

    1065    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
    1066        return f"AS {self.sql(expression, 'this')}"

def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str: View Source

    1068    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1069        desc = expression.args.get("desc")
    1070        if desc is not None:
    1071            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1072        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1073        options = f" {options}" if options else ""
    1074        return f"PRIMARY KEY{options}"

def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str: View Source

    1076    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1077        this = self.sql(expression, "this")
    1078        this = f" {this}" if this else ""
    1079        index_type = expression.args.get("index_type")
    1080        index_type = f" USING {index_type}" if index_type else ""
    1081        on_conflict = self.sql(expression, "on_conflict")
    1082        on_conflict = f" {on_conflict}" if on_conflict else ""
    1083        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1084        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1085        options = f" {options}" if options else ""
    1086        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"

def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str: View Source

    1088    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
    1089        return self.sql(expression, "this")

def create_sql(self, expression: sqlglot.expressions.Create) -> str: View Source

    1091    def create_sql(self, expression: exp.Create) -> str:
    1092        kind = self.sql(expression, "kind")
    1093        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1094        properties = expression.args.get("properties")
    1095        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1096
    1097        this = self.createable_sql(expression, properties_locs)
    1098
    1099        properties_sql = ""
    1100        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1101            exp.Properties.Location.POST_WITH
    1102        ):
    1103            properties_sql = self.sql(
    1104                exp.Properties(
    1105                    expressions=[
    1106                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1107                        *properties_locs[exp.Properties.Location.POST_WITH],
    1108                    ]
    1109                )
    1110            )
    1111
    1112            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1113                properties_sql = self.sep() + properties_sql
    1114            elif not self.pretty:
    1115                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1116                properties_sql = f" {properties_sql}"
    1117
    1118        begin = " BEGIN" if expression.args.get("begin") else ""
    1119        end = " END" if expression.args.get("end") else ""
    1120
    1121        expression_sql = self.sql(expression, "expression")
    1122        if expression_sql:
    1123            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1124
    1125            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1126                postalias_props_sql = ""
    1127                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1128                    postalias_props_sql = self.properties(
    1129                        exp.Properties(
    1130                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1131                        ),
    1132                        wrapped=False,
    1133                    )
    1134                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1135                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1136
    1137        postindex_props_sql = ""
    1138        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1139            postindex_props_sql = self.properties(
    1140                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1141                wrapped=False,
    1142                prefix=" ",
    1143            )
    1144
    1145        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1146        indexes = f" {indexes}" if indexes else ""
    1147        index_sql = indexes + postindex_props_sql
    1148
    1149        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1150        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1151        unique = " UNIQUE" if expression.args.get("unique") else ""
    1152
    1153        clustered = expression.args.get("clustered")
    1154        if clustered is None:
    1155            clustered_sql = ""
    1156        elif clustered:
    1157            clustered_sql = " CLUSTERED COLUMNSTORE"
    1158        else:
    1159            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1160
    1161        postcreate_props_sql = ""
    1162        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1163            postcreate_props_sql = self.properties(
    1164                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1165                sep=" ",
    1166                prefix=" ",
    1167                wrapped=False,
    1168            )
    1169
    1170        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1171
    1172        postexpression_props_sql = ""
    1173        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1174            postexpression_props_sql = self.properties(
    1175                exp.Properties(
    1176                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1177                ),
    1178                sep=" ",
    1179                prefix=" ",
    1180                wrapped=False,
    1181            )
    1182
    1183        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1184        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1185        no_schema_binding = (
    1186            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1187        )
    1188
    1189        clone = self.sql(expression, "clone")
    1190        clone = f" {clone}" if clone else ""
    1191
    1192        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1193            properties_expression = f"{expression_sql}{properties_sql}"
    1194        else:
    1195            properties_expression = f"{properties_sql}{expression_sql}"
    1196
    1197        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1198        return self.prepend_ctes(expression, expression_sql)

def sequenceproperties_sql(self, expression: sqlglot.expressions.SequenceProperties) -> str: View Source

    1200    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1201        start = self.sql(expression, "start")
    1202        start = f"START WITH {start}" if start else ""
    1203        increment = self.sql(expression, "increment")
    1204        increment = f" INCREMENT BY {increment}" if increment else ""
    1205        minvalue = self.sql(expression, "minvalue")
    1206        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1207        maxvalue = self.sql(expression, "maxvalue")
    1208        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1209        owned = self.sql(expression, "owned")
    1210        owned = f" OWNED BY {owned}" if owned else ""
    1211
    1212        cache = expression.args.get("cache")
    1213        if cache is None:
    1214            cache_str = ""
    1215        elif cache is True:
    1216            cache_str = " CACHE"
    1217        else:
    1218            cache_str = f" CACHE {cache}"
    1219
    1220        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1221        options = f" {options}" if options else ""
    1222
    1223        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()

def clone_sql(self, expression: sqlglot.expressions.Clone) -> str: View Source

    1225    def clone_sql(self, expression: exp.Clone) -> str:
    1226        this = self.sql(expression, "this")
    1227        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1228        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1229        return f"{shallow}{keyword} {this}"

def describe_sql(self, expression: sqlglot.expressions.Describe) -> str: View Source

    1231    def describe_sql(self, expression: exp.Describe) -> str:
    1232        style = expression.args.get("style")
    1233        style = f" {style}" if style else ""
    1234        partition = self.sql(expression, "partition")
    1235        partition = f" {partition}" if partition else ""
    1236        format = self.sql(expression, "format")
    1237        format = f" {format}" if format else ""
    1238
    1239        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"

def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str: View Source

    1241    def heredoc_sql(self, expression: exp.Heredoc) -> str:
    1242        tag = self.sql(expression, "tag")
    1243        return f"${tag}${self.sql(expression, 'this')}${tag}$"

def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str: View Source

    1245    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1246        with_ = self.sql(expression, "with")
    1247        if with_:
    1248            sql = f"{with_}{self.sep()}{sql}"
    1249        return sql

def with_sql(self, expression: sqlglot.expressions.With) -> str: View Source

    1251    def with_sql(self, expression: exp.With) -> str:
    1252        sql = self.expressions(expression, flat=True)
    1253        recursive = (
    1254            "RECURSIVE "
    1255            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1256            else ""
    1257        )
    1258        search = self.sql(expression, "search")
    1259        search = f" {search}" if search else ""
    1260
    1261        return f"WITH {recursive}{sql}{search}"

def cte_sql(self, expression: sqlglot.expressions.CTE) -> str: View Source

    1263    def cte_sql(self, expression: exp.CTE) -> str:
    1264        alias = expression.args.get("alias")
    1265        if alias:
    1266            alias.add_comments(expression.pop_comments())
    1267
    1268        alias_sql = self.sql(expression, "alias")
    1269
    1270        materialized = expression.args.get("materialized")
    1271        if materialized is False:
    1272            materialized = "NOT MATERIALIZED "
    1273        elif materialized:
    1274            materialized = "MATERIALIZED "
    1275
    1276        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"

def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str: View Source

    1278    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1279        alias = self.sql(expression, "this")
    1280        columns = self.expressions(expression, key="columns", flat=True)
    1281        columns = f"({columns})" if columns else ""
    1282
    1283        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1284            columns = ""
    1285            self.unsupported("Named columns are not supported in table alias.")
    1286
    1287        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1288            alias = self._next_name()
    1289
    1290        return f"{alias}{columns}"

def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str: View Source

    1292    def bitstring_sql(self, expression: exp.BitString) -> str:
    1293        this = self.sql(expression, "this")
    1294        if self.dialect.BIT_START:
    1295            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1296        return f"{int(this, 2)}"

def hexstring_sql( self, expression: sqlglot.expressions.HexString, binary_function_repr: Optional[str] = None) -> str: View Source

    1298    def hexstring_sql(
    1299        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
    1300    ) -> str:
    1301        this = self.sql(expression, "this")
    1302        is_integer_type = expression.args.get("is_integer")
    1303
    1304        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
    1305            not self.dialect.HEX_START and not binary_function_repr
    1306        ):
    1307            # Integer representation will be returned if:
    1308            # - The read dialect treats the hex value as integer literal but not the write
    1309            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
    1310            return f"{int(this, 16)}"
    1311
    1312        if not is_integer_type:
    1313            # Read dialect treats the hex value as BINARY/BLOB
    1314            if binary_function_repr:
    1315                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
    1316                return self.func(binary_function_repr, exp.Literal.string(this))
    1317            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
    1318                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
    1319                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
    1320
    1321        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"

def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str: View Source

    1323    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1324        this = self.sql(expression, "this")
    1325        if self.dialect.BYTE_START:
    1326            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1327        return this

def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str: View Source

    1329    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1330        this = self.sql(expression, "this")
    1331        escape = expression.args.get("escape")
    1332
    1333        if self.dialect.UNICODE_START:
    1334            escape_substitute = r"\\\1"
    1335            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1336        else:
    1337            escape_substitute = r"\\u\1"
    1338            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1339
    1340        if escape:
    1341            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1342            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1343        else:
    1344            escape_pattern = ESCAPED_UNICODE_RE
    1345            escape_sql = ""
    1346
    1347        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1348            this = escape_pattern.sub(escape_substitute, this)
    1349
    1350        return f"{left_quote}{this}{right_quote}{escape_sql}"

def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str: View Source

    1352    def rawstring_sql(self, expression: exp.RawString) -> str:
    1353        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
    1354        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"

def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str: View Source

    1356    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1357        this = self.sql(expression, "this")
    1358        specifier = self.sql(expression, "expression")
    1359        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1360        return f"{this}{specifier}"

def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str: View Source

    1362    def datatype_sql(self, expression: exp.DataType) -> str:
    1363        nested = ""
    1364        values = ""
    1365        interior = self.expressions(expression, flat=True)
    1366
    1367        type_value = expression.this
    1368        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1369            type_sql = self.sql(expression, "kind")
    1370        else:
    1371            type_sql = (
    1372                self.TYPE_MAPPING.get(type_value, type_value.value)
    1373                if isinstance(type_value, exp.DataType.Type)
    1374                else type_value
    1375            )
    1376
    1377        if interior:
    1378            if expression.args.get("nested"):
    1379                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1380                if expression.args.get("values") is not None:
    1381                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1382                    values = self.expressions(expression, key="values", flat=True)
    1383                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1384            elif type_value == exp.DataType.Type.INTERVAL:
    1385                nested = f" {interior}"
    1386            else:
    1387                nested = f"({interior})"
    1388
    1389        type_sql = f"{type_sql}{nested}{values}"
    1390        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1391            exp.DataType.Type.TIMETZ,
    1392            exp.DataType.Type.TIMESTAMPTZ,
    1393        ):
    1394            type_sql = f"{type_sql} WITH TIME ZONE"
    1395
    1396        return type_sql

def directory_sql(self, expression: sqlglot.expressions.Directory) -> str: View Source

    1398    def directory_sql(self, expression: exp.Directory) -> str:
    1399        local = "LOCAL " if expression.args.get("local") else ""
    1400        row_format = self.sql(expression, "row_format")
    1401        row_format = f" {row_format}" if row_format else ""
    1402        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

def delete_sql(self, expression: sqlglot.expressions.Delete) -> str: View Source

    1404    def delete_sql(self, expression: exp.Delete) -> str:
    1405        this = self.sql(expression, "this")
    1406        this = f" FROM {this}" if this else ""
    1407        using = self.sql(expression, "using")
    1408        using = f" USING {using}" if using else ""
    1409        cluster = self.sql(expression, "cluster")
    1410        cluster = f" {cluster}" if cluster else ""
    1411        where = self.sql(expression, "where")
    1412        returning = self.sql(expression, "returning")
    1413        limit = self.sql(expression, "limit")
    1414        tables = self.expressions(expression, key="tables")
    1415        tables = f" {tables}" if tables else ""
    1416        if self.RETURNING_END:
    1417            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1418        else:
    1419            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1420        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

def drop_sql(self, expression: sqlglot.expressions.Drop) -> str: View Source

    1422    def drop_sql(self, expression: exp.Drop) -> str:
    1423        this = self.sql(expression, "this")
    1424        expressions = self.expressions(expression, flat=True)
    1425        expressions = f" ({expressions})" if expressions else ""
    1426        kind = expression.args["kind"]
    1427        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1428        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1429        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1430        on_cluster = self.sql(expression, "cluster")
    1431        on_cluster = f" {on_cluster}" if on_cluster else ""
    1432        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1433        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1434        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1435        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1436        purge = " PURGE" if expression.args.get("purge") else ""
    1437        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"

def set_operation(self, expression: sqlglot.expressions.SetOperation) -> str: View Source

    1439    def set_operation(self, expression: exp.SetOperation) -> str:
    1440        op_type = type(expression)
    1441        op_name = op_type.key.upper()
    1442
    1443        distinct = expression.args.get("distinct")
    1444        if (
    1445            distinct is False
    1446            and op_type in (exp.Except, exp.Intersect)
    1447            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1448        ):
    1449            self.unsupported(f"{op_name} ALL is not supported")
    1450
    1451        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1452
    1453        if distinct is None:
    1454            distinct = default_distinct
    1455            if distinct is None:
    1456                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1457
    1458        if distinct is default_distinct:
    1459            distinct_or_all = ""
    1460        else:
    1461            distinct_or_all = " DISTINCT" if distinct else " ALL"
    1462
    1463        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
    1464        side_kind = f"{side_kind} " if side_kind else ""
    1465
    1466        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1467        on = self.expressions(expression, key="on", flat=True)
    1468        on = f" ON ({on})" if on else ""
    1469
    1470        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"

def set_operations(self, expression: sqlglot.expressions.SetOperation) -> str: View Source

    1472    def set_operations(self, expression: exp.SetOperation) -> str:
    1473        if not self.SET_OP_MODIFIERS:
    1474            limit = expression.args.get("limit")
    1475            order = expression.args.get("order")
    1476
    1477            if limit or order:
    1478                select = self._move_ctes_to_top_level(
    1479                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1480                )
    1481
    1482                if limit:
    1483                    select = select.limit(limit.pop(), copy=False)
    1484                if order:
    1485                    select = select.order_by(order.pop(), copy=False)
    1486                return self.sql(select)
    1487
    1488        sqls: t.List[str] = []
    1489        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1490
    1491        while stack:
    1492            node = stack.pop()
    1493
    1494            if isinstance(node, exp.SetOperation):
    1495                stack.append(node.expression)
    1496                stack.append(
    1497                    self.maybe_comment(
    1498                        self.set_operation(node), comments=node.comments, separated=True
    1499                    )
    1500                )
    1501                stack.append(node.this)
    1502            else:
    1503                sqls.append(self.sql(node))
    1504
    1505        this = self.sep().join(sqls)
    1506        this = self.query_modifiers(expression, this)
    1507        return self.prepend_ctes(expression, this)

def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str: View Source

    1509    def fetch_sql(self, expression: exp.Fetch) -> str:
    1510        direction = expression.args.get("direction")
    1511        direction = f" {direction}" if direction else ""
    1512        count = self.sql(expression, "count")
    1513        count = f" {count}" if count else ""
    1514        limit_options = self.sql(expression, "limit_options")
    1515        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
    1516        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"

def limitoptions_sql(self, expression: sqlglot.expressions.LimitOptions) -> str: View Source

    1518    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
    1519        percent = " PERCENT" if expression.args.get("percent") else ""
    1520        rows = " ROWS" if expression.args.get("rows") else ""
    1521        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
    1522        if not with_ties and rows:
    1523            with_ties = " ONLY"
    1524        return f"{percent}{rows}{with_ties}"

def filter_sql(self, expression: sqlglot.expressions.Filter) -> str: View Source

    1526    def filter_sql(self, expression: exp.Filter) -> str:
    1527        if self.AGGREGATE_FILTER_SUPPORTED:
    1528            this = self.sql(expression, "this")
    1529            where = self.sql(expression, "expression").strip()
    1530            return f"{this} FILTER({where})"
    1531
    1532        agg = expression.this
    1533        agg_arg = agg.this
    1534        cond = expression.expression.this
    1535        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1536        return self.sql(agg)

def hint_sql(self, expression: sqlglot.expressions.Hint) -> str: View Source

    1538    def hint_sql(self, expression: exp.Hint) -> str:
    1539        if not self.QUERY_HINTS:
    1540            self.unsupported("Hints are not supported")
    1541            return ""
    1542
    1543        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

def indexparameters_sql(self, expression: sqlglot.expressions.IndexParameters) -> str: View Source

    1545    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1546        using = self.sql(expression, "using")
    1547        using = f" USING {using}" if using else ""
    1548        columns = self.expressions(expression, key="columns", flat=True)
    1549        columns = f"({columns})" if columns else ""
    1550        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1551        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1552        where = self.sql(expression, "where")
    1553        include = self.expressions(expression, key="include", flat=True)
    1554        if include:
    1555            include = f" INCLUDE ({include})"
    1556        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1557        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1558        tablespace = self.sql(expression, "tablespace")
    1559        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1560        on = self.sql(expression, "on")
    1561        on = f" ON {on}" if on else ""
    1562
    1563        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"

def index_sql(self, expression: sqlglot.expressions.Index) -> str: View Source

    1565    def index_sql(self, expression: exp.Index) -> str:
    1566        unique = "UNIQUE " if expression.args.get("unique") else ""
    1567        primary = "PRIMARY " if expression.args.get("primary") else ""
    1568        amp = "AMP " if expression.args.get("amp") else ""
    1569        name = self.sql(expression, "this")
    1570        name = f"{name} " if name else ""
    1571        table = self.sql(expression, "table")
    1572        table = f"{self.INDEX_ON} {table}" if table else ""
    1573
    1574        index = "INDEX " if not table else ""
    1575
    1576        params = self.sql(expression, "params")
    1577        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str: View Source

    1579    def identifier_sql(self, expression: exp.Identifier) -> str:
    1580        text = expression.name
    1581        lower = text.lower()
    1582        text = lower if self.normalize and not expression.quoted else text
    1583        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1584        if (
    1585            expression.quoted
    1586            or self.dialect.can_identify(text, self.identify)
    1587            or lower in self.RESERVED_KEYWORDS
    1588            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1589        ):
    1590            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1591        return text

def hex_sql(self, expression: sqlglot.expressions.Hex) -> str: View Source

    1593    def hex_sql(self, expression: exp.Hex) -> str:
    1594        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1595        if self.dialect.HEX_LOWERCASE:
    1596            text = self.func("LOWER", text)
    1597
    1598        return text

def lowerhex_sql(self, expression: sqlglot.expressions.LowerHex) -> str: View Source

    1600    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1601        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1602        if not self.dialect.HEX_LOWERCASE:
    1603            text = self.func("LOWER", text)
    1604        return text

def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str: View Source

    1606    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1607        input_format = self.sql(expression, "input_format")
    1608        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1609        output_format = self.sql(expression, "output_format")
    1610        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1611        return self.sep().join((input_format, output_format))

def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str: View Source

    1613    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1614        string = self.sql(exp.Literal.string(expression.name))
    1615        return f"{prefix}{string}"

def partition_sql(self, expression: sqlglot.expressions.Partition) -> str: View Source

    1617    def partition_sql(self, expression: exp.Partition) -> str:
    1618        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
    1619        return f"{partition_keyword}({self.expressions(expression, flat=True)})"

def properties_sql(self, expression: sqlglot.expressions.Properties) -> str: View Source

    1621    def properties_sql(self, expression: exp.Properties) -> str:
    1622        root_properties = []
    1623        with_properties = []
    1624
    1625        for p in expression.expressions:
    1626            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1627            if p_loc == exp.Properties.Location.POST_WITH:
    1628                with_properties.append(p)
    1629            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1630                root_properties.append(p)
    1631
    1632        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1633        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1634
    1635        if root_props and with_props and not self.pretty:
    1636            with_props = " " + with_props
    1637
    1638        return root_props + with_props

def root_properties(self, properties: sqlglot.expressions.Properties) -> str: View Source

    1640    def root_properties(self, properties: exp.Properties) -> str:
    1641        if properties.expressions:
    1642            return self.expressions(properties, indent=False, sep=" ")
    1643        return ""

def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str: View Source

    1645    def properties(
    1646        self,
    1647        properties: exp.Properties,
    1648        prefix: str = "",
    1649        sep: str = ", ",
    1650        suffix: str = "",
    1651        wrapped: bool = True,
    1652    ) -> str:
    1653        if properties.expressions:
    1654            expressions = self.expressions(properties, sep=sep, indent=False)
    1655            if expressions:
    1656                expressions = self.wrap(expressions) if wrapped else expressions
    1657                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1658        return ""

def with_properties(self, properties: sqlglot.expressions.Properties) -> str: View Source

    1660    def with_properties(self, properties: exp.Properties) -> str:
    1661        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))

def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict: View Source

    1663    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1664        properties_locs = defaultdict(list)
    1665        for p in properties.expressions:
    1666            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1667            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1668                properties_locs[p_loc].append(p)
    1669            else:
    1670                self.unsupported(f"Unsupported property {p.key}")
    1671
    1672        return properties_locs

def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str: View Source

    1674    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1675        if isinstance(expression.this, exp.Dot):
    1676            return self.sql(expression, "this")
    1677        return f"'{expression.name}'" if string_key else expression.name

def property_sql(self, expression: sqlglot.expressions.Property) -> str: View Source

    1679    def property_sql(self, expression: exp.Property) -> str:
    1680        property_cls = expression.__class__
    1681        if property_cls == exp.Property:
    1682            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1683
    1684        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1685        if not property_name:
    1686            self.unsupported(f"Unsupported property {expression.key}")
    1687
    1688        return f"{property_name}={self.sql(expression, 'this')}"

def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str: View Source

    1690    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1691        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1692            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1693            options = f" {options}" if options else ""
    1694
    1695            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1696            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1697                like = f"({like})"
    1698
    1699            return like
    1700
    1701        if expression.expressions:
    1702            self.unsupported("Transpilation of LIKE property options is unsupported")
    1703
    1704        select = exp.select("*").from_(expression.this).limit(0)
    1705        return f"AS {self.sql(select)}"

def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str: View Source

    1707    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1708        no = "NO " if expression.args.get("no") else ""
    1709        protection = " PROTECTION" if expression.args.get("protection") else ""
    1710        return f"{no}FALLBACK{protection}"

def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str: View Source

    1712    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1713        no = "NO " if expression.args.get("no") else ""
    1714        local = expression.args.get("local")
    1715        local = f"{local} " if local else ""
    1716        dual = "DUAL " if expression.args.get("dual") else ""
    1717        before = "BEFORE " if expression.args.get("before") else ""
    1718        after = "AFTER " if expression.args.get("after") else ""
    1719        return f"{no}{local}{dual}{before}{after}JOURNAL"

def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str: View Source

    1721    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1722        freespace = self.sql(expression, "this")
    1723        percent = " PERCENT" if expression.args.get("percent") else ""
    1724        return f"FREESPACE={freespace}{percent}"

def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str: View Source

    1726    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1727        if expression.args.get("default"):
    1728            property = "DEFAULT"
    1729        elif expression.args.get("on"):
    1730            property = "ON"
    1731        else:
    1732            property = "OFF"
    1733        return f"CHECKSUM={property}"

def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str: View Source

    1735    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1736        if expression.args.get("no"):
    1737            return "NO MERGEBLOCKRATIO"
    1738        if expression.args.get("default"):
    1739            return "DEFAULT MERGEBLOCKRATIO"
    1740
    1741        percent = " PERCENT" if expression.args.get("percent") else ""
    1742        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str: View Source

    1744    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1745        default = expression.args.get("default")
    1746        minimum = expression.args.get("minimum")
    1747        maximum = expression.args.get("maximum")
    1748        if default or minimum or maximum:
    1749            if default:
    1750                prop = "DEFAULT"
    1751            elif minimum:
    1752                prop = "MINIMUM"
    1753            else:
    1754                prop = "MAXIMUM"
    1755            return f"{prop} DATABLOCKSIZE"
    1756        units = expression.args.get("units")
    1757        units = f" {units}" if units else ""
    1758        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str: View Source

    1760    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1761        autotemp = expression.args.get("autotemp")
    1762        always = expression.args.get("always")
    1763        default = expression.args.get("default")
    1764        manual = expression.args.get("manual")
    1765        never = expression.args.get("never")
    1766
    1767        if autotemp is not None:
    1768            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1769        elif always:
    1770            prop = "ALWAYS"
    1771        elif default:
    1772            prop = "DEFAULT"
    1773        elif manual:
    1774            prop = "MANUAL"
    1775        elif never:
    1776            prop = "NEVER"
    1777        return f"BLOCKCOMPRESSION={prop}"

def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str: View Source

    1779    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1780        no = expression.args.get("no")
    1781        no = " NO" if no else ""
    1782        concurrent = expression.args.get("concurrent")
    1783        concurrent = " CONCURRENT" if concurrent else ""
    1784        target = self.sql(expression, "target")
    1785        target = f" {target}" if target else ""
    1786        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str: View Source

    1788    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1789        if isinstance(expression.this, list):
    1790            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1791        if expression.this:
    1792            modulus = self.sql(expression, "this")
    1793            remainder = self.sql(expression, "expression")
    1794            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1795
    1796        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1797        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1798        return f"FROM ({from_expressions}) TO ({to_expressions})"

def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str: View Source

    1800    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1801        this = self.sql(expression, "this")
    1802
    1803        for_values_or_default = expression.expression
    1804        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1805            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1806        else:
    1807            for_values_or_default = " DEFAULT"
    1808
    1809        return f"PARTITION OF {this}{for_values_or_default}"

def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str: View Source

    1811    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1812        kind = expression.args.get("kind")
    1813        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1814        for_or_in = expression.args.get("for_or_in")
    1815        for_or_in = f" {for_or_in}" if for_or_in else ""
    1816        lock_type = expression.args.get("lock_type")
    1817        override = " OVERRIDE" if expression.args.get("override") else ""
    1818        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str: View Source

    1820    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1821        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1822        statistics = expression.args.get("statistics")
    1823        statistics_sql = ""
    1824        if statistics is not None:
    1825            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1826        return f"{data_sql}{statistics_sql}"

def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str: View Source

    1828    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1829        this = self.sql(expression, "this")
    1830        this = f"HISTORY_TABLE={this}" if this else ""
    1831        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1832        data_consistency = (
    1833            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1834        )
    1835        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1836        retention_period = (
    1837            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1838        )
    1839
    1840        if this:
    1841            on_sql = self.func("ON", this, data_consistency, retention_period)
    1842        else:
    1843            on_sql = "ON" if expression.args.get("on") else "OFF"
    1844
    1845        sql = f"SYSTEM_VERSIONING={on_sql}"
    1846
    1847        return f"WITH({sql})" if expression.args.get("with") else sql

def insert_sql(self, expression: sqlglot.expressions.Insert) -> str: View Source

    1849    def insert_sql(self, expression: exp.Insert) -> str:
    1850        hint = self.sql(expression, "hint")
    1851        overwrite = expression.args.get("overwrite")
    1852
    1853        if isinstance(expression.this, exp.Directory):
    1854            this = " OVERWRITE" if overwrite else " INTO"
    1855        else:
    1856            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1857
    1858        stored = self.sql(expression, "stored")
    1859        stored = f" {stored}" if stored else ""
    1860        alternative = expression.args.get("alternative")
    1861        alternative = f" OR {alternative}" if alternative else ""
    1862        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1863        is_function = expression.args.get("is_function")
    1864        if is_function:
    1865            this = f"{this} FUNCTION"
    1866        this = f"{this} {self.sql(expression, 'this')}"
    1867
    1868        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1869        where = self.sql(expression, "where")
    1870        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1871        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1872        on_conflict = self.sql(expression, "conflict")
    1873        on_conflict = f" {on_conflict}" if on_conflict else ""
    1874        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1875        returning = self.sql(expression, "returning")
    1876
    1877        if self.RETURNING_END:
    1878            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1879        else:
    1880            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1881
    1882        partition_by = self.sql(expression, "partition")
    1883        partition_by = f" {partition_by}" if partition_by else ""
    1884        settings = self.sql(expression, "settings")
    1885        settings = f" {settings}" if settings else ""
    1886
    1887        source = self.sql(expression, "source")
    1888        source = f"TABLE {source}" if source else ""
    1889
    1890        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1891        return self.prepend_ctes(expression, sql)

def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str: View Source

    1893    def introducer_sql(self, expression: exp.Introducer) -> str:
    1894        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"

def kill_sql(self, expression: sqlglot.expressions.Kill) -> str: View Source

    1896    def kill_sql(self, expression: exp.Kill) -> str:
    1897        kind = self.sql(expression, "kind")
    1898        kind = f" {kind}" if kind else ""
    1899        this = self.sql(expression, "this")
    1900        this = f" {this}" if this else ""
    1901        return f"KILL{kind}{this}"

def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str: View Source

    1903    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1904        return expression.name

def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str: View Source

    1906    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1907        return expression.name

def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str: View Source

    1909    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1910        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1911
    1912        constraint = self.sql(expression, "constraint")
    1913        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1914
    1915        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1916        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1917        action = self.sql(expression, "action")
    1918
    1919        expressions = self.expressions(expression, flat=True)
    1920        if expressions:
    1921            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1922            expressions = f" {set_keyword}{expressions}"
    1923
    1924        where = self.sql(expression, "where")
    1925        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"

def returning_sql(self, expression: sqlglot.expressions.Returning) -> str: View Source

    1927    def returning_sql(self, expression: exp.Returning) -> str:
    1928        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"

def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str: View Source

    1930    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1931        fields = self.sql(expression, "fields")
    1932        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1933        escaped = self.sql(expression, "escaped")
    1934        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1935        items = self.sql(expression, "collection_items")
    1936        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1937        keys = self.sql(expression, "map_keys")
    1938        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1939        lines = self.sql(expression, "lines")
    1940        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1941        null = self.sql(expression, "null")
    1942        null = f" NULL DEFINED AS {null}" if null else ""
    1943        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str: View Source

    1945    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1946        return f"WITH ({self.expressions(expression, flat=True)})"

def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str: View Source

    1948    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1949        this = f"{self.sql(expression, 'this')} INDEX"
    1950        target = self.sql(expression, "target")
    1951        target = f" FOR {target}" if target else ""
    1952        return f"{this}{target} ({self.expressions(expression, flat=True)})"

def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str: View Source

    1954    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1955        this = self.sql(expression, "this")
    1956        kind = self.sql(expression, "kind")
    1957        expr = self.sql(expression, "expression")
    1958        return f"{this} ({kind} => {expr})"

def table_parts(self, expression: sqlglot.expressions.Table) -> str: View Source

    1960    def table_parts(self, expression: exp.Table) -> str:
    1961        return ".".join(
    1962            self.sql(part)
    1963            for part in (
    1964                expression.args.get("catalog"),
    1965                expression.args.get("db"),
    1966                expression.args.get("this"),
    1967            )
    1968            if part is not None
    1969        )

def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str: View Source

    1971    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1972        table = self.table_parts(expression)
    1973        only = "ONLY " if expression.args.get("only") else ""
    1974        partition = self.sql(expression, "partition")
    1975        partition = f" {partition}" if partition else ""
    1976        version = self.sql(expression, "version")
    1977        version = f" {version}" if version else ""
    1978        alias = self.sql(expression, "alias")
    1979        alias = f"{sep}{alias}" if alias else ""
    1980
    1981        sample = self.sql(expression, "sample")
    1982        if self.dialect.ALIAS_POST_TABLESAMPLE:
    1983            sample_pre_alias = sample
    1984            sample_post_alias = ""
    1985        else:
    1986            sample_pre_alias = ""
    1987            sample_post_alias = sample
    1988
    1989        hints = self.expressions(expression, key="hints", sep=" ")
    1990        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1991        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    1992        joins = self.indent(
    1993            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1994        )
    1995        laterals = self.expressions(expression, key="laterals", sep="")
    1996
    1997        file_format = self.sql(expression, "format")
    1998        if file_format:
    1999            pattern = self.sql(expression, "pattern")
    2000            pattern = f", PATTERN => {pattern}" if pattern else ""
    2001            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    2002
    2003        ordinality = expression.args.get("ordinality") or ""
    2004        if ordinality:
    2005            ordinality = f" WITH ORDINALITY{alias}"
    2006            alias = ""
    2007
    2008        when = self.sql(expression, "when")
    2009        if when:
    2010            table = f"{table} {when}"
    2011
    2012        changes = self.sql(expression, "changes")
    2013        changes = f" {changes}" if changes else ""
    2014
    2015        rows_from = self.expressions(expression, key="rows_from")
    2016        if rows_from:
    2017            table = f"ROWS FROM {self.wrap(rows_from)}"
    2018
    2019        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"

def tablefromrows_sql(self, expression: sqlglot.expressions.TableFromRows) -> str: View Source

    2021    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
    2022        table = self.func("TABLE", expression.this)
    2023        alias = self.sql(expression, "alias")
    2024        alias = f" AS {alias}" if alias else ""
    2025        sample = self.sql(expression, "sample")
    2026        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2027        joins = self.indent(
    2028            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    2029        )
    2030        return f"{table}{alias}{pivots}{sample}{joins}"

def tablesample_sql( self, expression: sqlglot.expressions.TableSample, tablesample_keyword: Optional[str] = None) -> str: View Source

    2032    def tablesample_sql(
    2033        self,
    2034        expression: exp.TableSample,
    2035        tablesample_keyword: t.Optional[str] = None,
    2036    ) -> str:
    2037        method = self.sql(expression, "method")
    2038        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    2039        numerator = self.sql(expression, "bucket_numerator")
    2040        denominator = self.sql(expression, "bucket_denominator")
    2041        field = self.sql(expression, "bucket_field")
    2042        field = f" ON {field}" if field else ""
    2043        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    2044        seed = self.sql(expression, "seed")
    2045        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    2046
    2047        size = self.sql(expression, "size")
    2048        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    2049            size = f"{size} ROWS"
    2050
    2051        percent = self.sql(expression, "percent")
    2052        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    2053            percent = f"{percent} PERCENT"
    2054
    2055        expr = f"{bucket}{percent}{size}"
    2056        if self.TABLESAMPLE_REQUIRES_PARENS:
    2057            expr = f"({expr})"
    2058
    2059        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"

def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str: View Source

    2061    def pivot_sql(self, expression: exp.Pivot) -> str:
    2062        expressions = self.expressions(expression, flat=True)
    2063        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2064
    2065        group = self.sql(expression, "group")
    2066
    2067        if expression.this:
    2068            this = self.sql(expression, "this")
    2069            if not expressions:
    2070                return f"UNPIVOT {this}"
    2071
    2072            on = f"{self.seg('ON')} {expressions}"
    2073            into = self.sql(expression, "into")
    2074            into = f"{self.seg('INTO')} {into}" if into else ""
    2075            using = self.expressions(expression, key="using", flat=True)
    2076            using = f"{self.seg('USING')} {using}" if using else ""
    2077            return f"{direction} {this}{on}{into}{using}{group}"
    2078
    2079        alias = self.sql(expression, "alias")
    2080        alias = f" AS {alias}" if alias else ""
    2081
    2082        fields = self.expressions(
    2083            expression,
    2084            "fields",
    2085            sep=" ",
    2086            dynamic=True,
    2087            new_line=True,
    2088            skip_first=True,
    2089            skip_last=True,
    2090        )
    2091
    2092        include_nulls = expression.args.get("include_nulls")
    2093        if include_nulls is not None:
    2094            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2095        else:
    2096            nulls = ""
    2097
    2098        default_on_null = self.sql(expression, "default_on_null")
    2099        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2100        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"

def version_sql(self, expression: sqlglot.expressions.Version) -> str: View Source

    2102    def version_sql(self, expression: exp.Version) -> str:
    2103        this = f"FOR {expression.name}"
    2104        kind = expression.text("kind")
    2105        expr = self.sql(expression, "expression")
    2106        return f"{this} {kind} {expr}"

def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str: View Source

    2108    def tuple_sql(self, expression: exp.Tuple) -> str:
    2109        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"

def update_sql(self, expression: sqlglot.expressions.Update) -> str: View Source

    2111    def update_sql(self, expression: exp.Update) -> str:
    2112        this = self.sql(expression, "this")
    2113        set_sql = self.expressions(expression, flat=True)
    2114        from_sql = self.sql(expression, "from")
    2115        where_sql = self.sql(expression, "where")
    2116        returning = self.sql(expression, "returning")
    2117        order = self.sql(expression, "order")
    2118        limit = self.sql(expression, "limit")
    2119        if self.RETURNING_END:
    2120            expression_sql = f"{from_sql}{where_sql}{returning}"
    2121        else:
    2122            expression_sql = f"{returning}{from_sql}{where_sql}"
    2123        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2124        return self.prepend_ctes(expression, sql)

def values_sql( self, expression: sqlglot.expressions.Values, values_as_table: bool = True) -> str: View Source

    2126    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2127        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2128
    2129        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2130        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2131            args = self.expressions(expression)
    2132            alias = self.sql(expression, "alias")
    2133            values = f"VALUES{self.seg('')}{args}"
    2134            values = (
    2135                f"({values})"
    2136                if self.WRAP_DERIVED_VALUES
    2137                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2138                else values
    2139            )
    2140            return f"{values} AS {alias}" if alias else values
    2141
    2142        # Converts `VALUES...` expression into a series of select unions.
    2143        alias_node = expression.args.get("alias")
    2144        column_names = alias_node and alias_node.columns
    2145
    2146        selects: t.List[exp.Query] = []
    2147
    2148        for i, tup in enumerate(expression.expressions):
    2149            row = tup.expressions
    2150
    2151            if i == 0 and column_names:
    2152                row = [
    2153                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2154                ]
    2155
    2156            selects.append(exp.Select(expressions=row))
    2157
    2158        if self.pretty:
    2159            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2160            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2161            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2162            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2163            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2164
    2165        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2166        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2167        return f"({unions}){alias}"

def var_sql(self, expression: sqlglot.expressions.Var) -> str: View Source

    2169    def var_sql(self, expression: exp.Var) -> str:
    2170        return self.sql(expression, "this")

@unsupported_args('expressions')
def into_sql(self, expression: sqlglot.expressions.Into) -> str: View Source

    2172    @unsupported_args("expressions")
    2173    def into_sql(self, expression: exp.Into) -> str:
    2174        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2175        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2176        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"

def from_sql(self, expression: sqlglot.expressions.From) -> str: View Source

    2178    def from_sql(self, expression: exp.From) -> str:
    2179        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"

def groupingsets_sql(self, expression: sqlglot.expressions.GroupingSets) -> str: View Source

    2181    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
    2182        grouping_sets = self.expressions(expression, indent=False)
    2183        return f"GROUPING SETS {self.wrap(grouping_sets)}"

def rollup_sql(self, expression: sqlglot.expressions.Rollup) -> str: View Source

    2185    def rollup_sql(self, expression: exp.Rollup) -> str:
    2186        expressions = self.expressions(expression, indent=False)
    2187        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"

def cube_sql(self, expression: sqlglot.expressions.Cube) -> str: View Source

    2189    def cube_sql(self, expression: exp.Cube) -> str:
    2190        expressions = self.expressions(expression, indent=False)
    2191        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"

def group_sql(self, expression: sqlglot.expressions.Group) -> str: View Source

    2193    def group_sql(self, expression: exp.Group) -> str:
    2194        group_by_all = expression.args.get("all")
    2195        if group_by_all is True:
    2196            modifier = " ALL"
    2197        elif group_by_all is False:
    2198            modifier = " DISTINCT"
    2199        else:
    2200            modifier = ""
    2201
    2202        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2203
    2204        grouping_sets = self.expressions(expression, key="grouping_sets")
    2205        cube = self.expressions(expression, key="cube")
    2206        rollup = self.expressions(expression, key="rollup")
    2207
    2208        groupings = csv(
    2209            self.seg(grouping_sets) if grouping_sets else "",
    2210            self.seg(cube) if cube else "",
    2211            self.seg(rollup) if rollup else "",
    2212            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2213            sep=self.GROUPINGS_SEP,
    2214        )
    2215
    2216        if (
    2217            expression.expressions
    2218            and groupings
    2219            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2220        ):
    2221            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2222
    2223        return f"{group_by}{groupings}"

def having_sql(self, expression: sqlglot.expressions.Having) -> str: View Source

    2225    def having_sql(self, expression: exp.Having) -> str:
    2226        this = self.indent(self.sql(expression, "this"))
    2227        return f"{self.seg('HAVING')}{self.sep()}{this}"

def connect_sql(self, expression: sqlglot.expressions.Connect) -> str: View Source

    2229    def connect_sql(self, expression: exp.Connect) -> str:
    2230        start = self.sql(expression, "start")
    2231        start = self.seg(f"START WITH {start}") if start else ""
    2232        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2233        connect = self.sql(expression, "connect")
    2234        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2235        return start + connect

def prior_sql(self, expression: sqlglot.expressions.Prior) -> str: View Source

    2237    def prior_sql(self, expression: exp.Prior) -> str:
    2238        return f"PRIOR {self.sql(expression, 'this')}"

def join_sql(self, expression: sqlglot.expressions.Join) -> str: View Source

    2240    def join_sql(self, expression: exp.Join) -> str:
    2241        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2242            side = None
    2243        else:
    2244            side = expression.side
    2245
    2246        op_sql = " ".join(
    2247            op
    2248            for op in (
    2249                expression.method,
    2250                "GLOBAL" if expression.args.get("global") else None,
    2251                side,
    2252                expression.kind,
    2253                expression.hint if self.JOIN_HINTS else None,
    2254            )
    2255            if op
    2256        )
    2257        match_cond = self.sql(expression, "match_condition")
    2258        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2259        on_sql = self.sql(expression, "on")
    2260        using = expression.args.get("using")
    2261
    2262        if not on_sql and using:
    2263            on_sql = csv(*(self.sql(column) for column in using))
    2264
    2265        this = expression.this
    2266        this_sql = self.sql(this)
    2267
    2268        exprs = self.expressions(expression)
    2269        if exprs:
    2270            this_sql = f"{this_sql},{self.seg(exprs)}"
    2271
    2272        if on_sql:
    2273            on_sql = self.indent(on_sql, skip_first=True)
    2274            space = self.seg(" " * self.pad) if self.pretty else " "
    2275            if using:
    2276                on_sql = f"{space}USING ({on_sql})"
    2277            else:
    2278                on_sql = f"{space}ON {on_sql}"
    2279        elif not op_sql:
    2280            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2281                return f" {this_sql}"
    2282
    2283            return f", {this_sql}"
    2284
    2285        if op_sql != "STRAIGHT_JOIN":
    2286            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2287
    2288        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"

def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str: View Source

    2290    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    2291        args = self.expressions(expression, flat=True)
    2292        args = f"({args})" if len(args.split(",")) > 1 else args
    2293        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"

def lateral_op(self, expression: sqlglot.expressions.Lateral) -> str: View Source

    2295    def lateral_op(self, expression: exp.Lateral) -> str:
    2296        cross_apply = expression.args.get("cross_apply")
    2297
    2298        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2299        if cross_apply is True:
    2300            op = "INNER JOIN "
    2301        elif cross_apply is False:
    2302            op = "LEFT JOIN "
    2303        else:
    2304            op = ""
    2305
    2306        return f"{op}LATERAL"

def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str: View Source

    2308    def lateral_sql(self, expression: exp.Lateral) -> str:
    2309        this = self.sql(expression, "this")
    2310
    2311        if expression.args.get("view"):
    2312            alias = expression.args["alias"]
    2313            columns = self.expressions(alias, key="columns", flat=True)
    2314            table = f" {alias.name}" if alias.name else ""
    2315            columns = f" AS {columns}" if columns else ""
    2316            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2317            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2318
    2319        alias = self.sql(expression, "alias")
    2320        alias = f" AS {alias}" if alias else ""
    2321
    2322        ordinality = expression.args.get("ordinality") or ""
    2323        if ordinality:
    2324            ordinality = f" WITH ORDINALITY{alias}"
    2325            alias = ""
    2326
    2327        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"

def limit_sql(self, expression: sqlglot.expressions.Limit, top: bool = False) -> str: View Source

    2329    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2330        this = self.sql(expression, "this")
    2331
    2332        args = [
    2333            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2334            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2335            if e
    2336        ]
    2337
    2338        args_sql = ", ".join(self.sql(e) for e in args)
    2339        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2340        expressions = self.expressions(expression, flat=True)
    2341        limit_options = self.sql(expression, "limit_options")
    2342        expressions = f" BY {expressions}" if expressions else ""
    2343
    2344        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"

def offset_sql(self, expression: sqlglot.expressions.Offset) -> str: View Source

    2346    def offset_sql(self, expression: exp.Offset) -> str:
    2347        this = self.sql(expression, "this")
    2348        value = expression.expression
    2349        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2350        expressions = self.expressions(expression, flat=True)
    2351        expressions = f" BY {expressions}" if expressions else ""
    2352        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str: View Source

    2354    def setitem_sql(self, expression: exp.SetItem) -> str:
    2355        kind = self.sql(expression, "kind")
    2356        kind = f"{kind} " if kind else ""
    2357        this = self.sql(expression, "this")
    2358        expressions = self.expressions(expression)
    2359        collate = self.sql(expression, "collate")
    2360        collate = f" COLLATE {collate}" if collate else ""
    2361        global_ = "GLOBAL " if expression.args.get("global") else ""
    2362        return f"{global_}{kind}{this}{expressions}{collate}"

def set_sql(self, expression: sqlglot.expressions.Set) -> str: View Source

    2364    def set_sql(self, expression: exp.Set) -> str:
    2365        expressions = f" {self.expressions(expression, flat=True)}"
    2366        tag = " TAG" if expression.args.get("tag") else ""
    2367        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str: View Source

    2369    def pragma_sql(self, expression: exp.Pragma) -> str:
    2370        return f"PRAGMA {self.sql(expression, 'this')}"

def lock_sql(self, expression: sqlglot.expressions.Lock) -> str: View Source

    2372    def lock_sql(self, expression: exp.Lock) -> str:
    2373        if not self.LOCKING_READS_SUPPORTED:
    2374            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2375            return ""
    2376
    2377        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2378        expressions = self.expressions(expression, flat=True)
    2379        expressions = f" OF {expressions}" if expressions else ""
    2380        wait = expression.args.get("wait")
    2381
    2382        if wait is not None:
    2383            if isinstance(wait, exp.Literal):
    2384                wait = f" WAIT {self.sql(wait)}"
    2385            else:
    2386                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2387
    2388        return f"{lock_type}{expressions}{wait or ''}"

def literal_sql(self, expression: sqlglot.expressions.Literal) -> str: View Source

    2390    def literal_sql(self, expression: exp.Literal) -> str:
    2391        text = expression.this or ""
    2392        if expression.is_string:
    2393            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2394        return text

def escape_str(self, text: str, escape_backslash: bool = True) -> str: View Source

    2396    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2397        if self.dialect.ESCAPED_SEQUENCES:
    2398            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2399            text = "".join(
    2400                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2401            )
    2402
    2403        return self._replace_line_breaks(text).replace(
    2404            self.dialect.QUOTE_END, self._escaped_quote_end
    2405        )

def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str: View Source

    2407    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2408        local = " LOCAL" if expression.args.get("local") else ""
    2409        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2410        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2411        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2412        partition = self.sql(expression, "partition")
    2413        partition = f" {partition}" if partition else ""
    2414        input_format = self.sql(expression, "input_format")
    2415        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2416        serde = self.sql(expression, "serde")
    2417        serde = f" SERDE {serde}" if serde else ""
    2418        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

def null_sql(self, *_) -> str: View Source

    2420    def null_sql(self, *_) -> str:
    2421        return "NULL"

def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str: View Source

    2423    def boolean_sql(self, expression: exp.Boolean) -> str:
    2424        return "TRUE" if expression.this else "FALSE"

def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str: View Source

    2426    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2427        this = self.sql(expression, "this")
    2428        this = f"{this} " if this else this
    2429        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2430        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore

def withfill_sql(self, expression: sqlglot.expressions.WithFill) -> str: View Source

    2432    def withfill_sql(self, expression: exp.WithFill) -> str:
    2433        from_sql = self.sql(expression, "from")
    2434        from_sql = f" FROM {from_sql}" if from_sql else ""
    2435        to_sql = self.sql(expression, "to")
    2436        to_sql = f" TO {to_sql}" if to_sql else ""
    2437        step_sql = self.sql(expression, "step")
    2438        step_sql = f" STEP {step_sql}" if step_sql else ""
    2439        interpolated_values = [
    2440            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2441            if isinstance(e, exp.Alias)
    2442            else self.sql(e, "this")
    2443            for e in expression.args.get("interpolate") or []
    2444        ]
    2445        interpolate = (
    2446            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2447        )
    2448        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"

def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str: View Source

    2450    def cluster_sql(self, expression: exp.Cluster) -> str:
    2451        return self.op_expressions("CLUSTER BY", expression)

def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str: View Source

    2453    def distribute_sql(self, expression: exp.Distribute) -> str:
    2454        return self.op_expressions("DISTRIBUTE BY", expression)

def sort_sql(self, expression: sqlglot.expressions.Sort) -> str: View Source

    2456    def sort_sql(self, expression: exp.Sort) -> str:
    2457        return self.op_expressions("SORT BY", expression)

def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str: View Source

    2459    def ordered_sql(self, expression: exp.Ordered) -> str:
    2460        desc = expression.args.get("desc")
    2461        asc = not desc
    2462
    2463        nulls_first = expression.args.get("nulls_first")
    2464        nulls_last = not nulls_first
    2465        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2466        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2467        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2468
    2469        this = self.sql(expression, "this")
    2470
    2471        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2472        nulls_sort_change = ""
    2473        if nulls_first and (
    2474            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2475        ):
    2476            nulls_sort_change = " NULLS FIRST"
    2477        elif (
    2478            nulls_last
    2479            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2480            and not nulls_are_last
    2481        ):
    2482            nulls_sort_change = " NULLS LAST"
    2483
    2484        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2485        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2486            window = expression.find_ancestor(exp.Window, exp.Select)
    2487            if isinstance(window, exp.Window) and window.args.get("spec"):
    2488                self.unsupported(
    2489                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2490                )
    2491                nulls_sort_change = ""
    2492            elif self.NULL_ORDERING_SUPPORTED is False and (
    2493                (asc and nulls_sort_change == " NULLS LAST")
    2494                or (desc and nulls_sort_change == " NULLS FIRST")
    2495            ):
    2496                # BigQuery does not allow these ordering/nulls combinations when used under
    2497                # an aggregation func or under a window containing one
    2498                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2499
    2500                if isinstance(ancestor, exp.Window):
    2501                    ancestor = ancestor.this
    2502                if isinstance(ancestor, exp.AggFunc):
    2503                    self.unsupported(
    2504                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2505                    )
    2506                    nulls_sort_change = ""
    2507            elif self.NULL_ORDERING_SUPPORTED is None:
    2508                if expression.this.is_int:
    2509                    self.unsupported(
    2510                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2511                    )
    2512                elif not isinstance(expression.this, exp.Rand):
    2513                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2514                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2515                nulls_sort_change = ""
    2516
    2517        with_fill = self.sql(expression, "with_fill")
    2518        with_fill = f" {with_fill}" if with_fill else ""
    2519
    2520        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

def matchrecognizemeasure_sql(self, expression: sqlglot.expressions.MatchRecognizeMeasure) -> str: View Source

    2522    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2523        window_frame = self.sql(expression, "window_frame")
    2524        window_frame = f"{window_frame} " if window_frame else ""
    2525
    2526        this = self.sql(expression, "this")
    2527
    2528        return f"{window_frame}{this}"

def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str: View Source

    2530    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2531        partition = self.partition_by_sql(expression)
    2532        order = self.sql(expression, "order")
    2533        measures = self.expressions(expression, key="measures")
    2534        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2535        rows = self.sql(expression, "rows")
    2536        rows = self.seg(rows) if rows else ""
    2537        after = self.sql(expression, "after")
    2538        after = self.seg(after) if after else ""
    2539        pattern = self.sql(expression, "pattern")
    2540        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2541        definition_sqls = [
    2542            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2543            for definition in expression.args.get("define", [])
    2544        ]
    2545        definitions = self.expressions(sqls=definition_sqls)
    2546        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2547        body = "".join(
    2548            (
    2549                partition,
    2550                order,
    2551                measures,
    2552                rows,
    2553                after,
    2554                pattern,
    2555                define,
    2556            )
    2557        )
    2558        alias = self.sql(expression, "alias")
    2559        alias = f" {alias}" if alias else ""
    2560        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str: View Source

    2562    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2563        limit = expression.args.get("limit")
    2564
    2565        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2566            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2567        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2568            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2569
    2570        return csv(
    2571            *sqls,
    2572            *[self.sql(join) for join in expression.args.get("joins") or []],
    2573            self.sql(expression, "match"),
    2574            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2575            self.sql(expression, "prewhere"),
    2576            self.sql(expression, "where"),
    2577            self.sql(expression, "connect"),
    2578            self.sql(expression, "group"),
    2579            self.sql(expression, "having"),
    2580            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2581            self.sql(expression, "order"),
    2582            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2583            *self.after_limit_modifiers(expression),
    2584            self.options_modifier(expression),
    2585            sep="",
    2586        )

def options_modifier(self, expression: sqlglot.expressions.Expression) -> str: View Source

    2588    def options_modifier(self, expression: exp.Expression) -> str:
    2589        options = self.expressions(expression, key="options")
    2590        return f" {options}" if options else ""

def queryoption_sql(self, expression: sqlglot.expressions.QueryOption) -> str: View Source

    2592    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2593        self.unsupported("Unsupported query option.")
    2594        return ""

def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]: View Source

    2596    def offset_limit_modifiers(
    2597        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2598    ) -> t.List[str]:
    2599        return [
    2600            self.sql(expression, "offset") if fetch else self.sql(limit),
    2601            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2602        ]

def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]: View Source

    2604    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    2605        locks = self.expressions(expression, key="locks", sep=" ")
    2606        locks = f" {locks}" if locks else ""
    2607        return [locks, self.sql(expression, "sample")]

def select_sql(self, expression: sqlglot.expressions.Select) -> str: View Source

    2609    def select_sql(self, expression: exp.Select) -> str:
    2610        into = expression.args.get("into")
    2611        if not self.SUPPORTS_SELECT_INTO and into:
    2612            into.pop()
    2613
    2614        hint = self.sql(expression, "hint")
    2615        distinct = self.sql(expression, "distinct")
    2616        distinct = f" {distinct}" if distinct else ""
    2617        kind = self.sql(expression, "kind")
    2618
    2619        limit = expression.args.get("limit")
    2620        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2621            top = self.limit_sql(limit, top=True)
    2622            limit.pop()
    2623        else:
    2624            top = ""
    2625
    2626        expressions = self.expressions(expression)
    2627
    2628        if kind:
    2629            if kind in self.SELECT_KINDS:
    2630                kind = f" AS {kind}"
    2631            else:
    2632                if kind == "STRUCT":
    2633                    expressions = self.expressions(
    2634                        sqls=[
    2635                            self.sql(
    2636                                exp.Struct(
    2637                                    expressions=[
    2638                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2639                                        if isinstance(e, exp.Alias)
    2640                                        else e
    2641                                        for e in expression.expressions
    2642                                    ]
    2643                                )
    2644                            )
    2645                        ]
    2646                    )
    2647                kind = ""
    2648
    2649        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2650        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2651
    2652        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2653        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2654        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2655        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2656        sql = self.query_modifiers(
    2657            expression,
    2658            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2659            self.sql(expression, "into", comment=False),
    2660            self.sql(expression, "from", comment=False),
    2661        )
    2662
    2663        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2664        if expression.args.get("with"):
    2665            sql = self.maybe_comment(sql, expression)
    2666            expression.pop_comments()
    2667
    2668        sql = self.prepend_ctes(expression, sql)
    2669
    2670        if not self.SUPPORTS_SELECT_INTO and into:
    2671            if into.args.get("temporary"):
    2672                table_kind = " TEMPORARY"
    2673            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2674                table_kind = " UNLOGGED"
    2675            else:
    2676                table_kind = ""
    2677            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2678
    2679        return sql

def schema_sql(self, expression: sqlglot.expressions.Schema) -> str: View Source

    2681    def schema_sql(self, expression: exp.Schema) -> str:
    2682        this = self.sql(expression, "this")
    2683        sql = self.schema_columns_sql(expression)
    2684        return f"{this} {sql}" if this and sql else this or sql

def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str: View Source

    2686    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2687        if expression.expressions:
    2688            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2689        return ""

def star_sql(self, expression: sqlglot.expressions.Star) -> str: View Source

    2691    def star_sql(self, expression: exp.Star) -> str:
    2692        except_ = self.expressions(expression, key="except", flat=True)
    2693        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2694        replace = self.expressions(expression, key="replace", flat=True)
    2695        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2696        rename = self.expressions(expression, key="rename", flat=True)
    2697        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2698        return f"*{except_}{replace}{rename}"

def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str: View Source

    2700    def parameter_sql(self, expression: exp.Parameter) -> str:
    2701        this = self.sql(expression, "this")
    2702        return f"{self.PARAMETER_TOKEN}{this}"

def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str: View Source

    2704    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2705        this = self.sql(expression, "this")
    2706        kind = expression.text("kind")
    2707        if kind:
    2708            kind = f"{kind}."
    2709        return f"@@{kind}{this}"

def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str: View Source

    2711    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2712        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"

def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str: View Source

    2714    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2715        alias = self.sql(expression, "alias")
    2716        alias = f"{sep}{alias}" if alias else ""
    2717        sample = self.sql(expression, "sample")
    2718        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2719            alias = f"{sample}{alias}"
    2720
    2721            # Set to None so it's not generated again by self.query_modifiers()
    2722            expression.set("sample", None)
    2723
    2724        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2725        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2726        return self.prepend_ctes(expression, sql)

def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str: View Source

    2728    def qualify_sql(self, expression: exp.Qualify) -> str:
    2729        this = self.indent(self.sql(expression, "this"))
    2730        return f"{self.seg('QUALIFY')}{self.sep()}{this}"

def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str: View Source

    2732    def unnest_sql(self, expression: exp.Unnest) -> str:
    2733        args = self.expressions(expression, flat=True)
    2734
    2735        alias = expression.args.get("alias")
    2736        offset = expression.args.get("offset")
    2737
    2738        if self.UNNEST_WITH_ORDINALITY:
    2739            if alias and isinstance(offset, exp.Expression):
    2740                alias.append("columns", offset)
    2741
    2742        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2743            columns = alias.columns
    2744            alias = self.sql(columns[0]) if columns else ""
    2745        else:
    2746            alias = self.sql(alias)
    2747
    2748        alias = f" AS {alias}" if alias else alias
    2749        if self.UNNEST_WITH_ORDINALITY:
    2750            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2751        else:
    2752            if isinstance(offset, exp.Expression):
    2753                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2754            elif offset:
    2755                suffix = f"{alias} WITH OFFSET"
    2756            else:
    2757                suffix = alias
    2758
    2759        return f"UNNEST({args}){suffix}"

def prewhere_sql(self, expression: sqlglot.expressions.PreWhere) -> str: View Source

    2761    def prewhere_sql(self, expression: exp.PreWhere) -> str:
    2762        return ""

def where_sql(self, expression: sqlglot.expressions.Where) -> str: View Source

    2764    def where_sql(self, expression: exp.Where) -> str:
    2765        this = self.indent(self.sql(expression, "this"))
    2766        return f"{self.seg('WHERE')}{self.sep()}{this}"

def window_sql(self, expression: sqlglot.expressions.Window) -> str: View Source

    2768    def window_sql(self, expression: exp.Window) -> str:
    2769        this = self.sql(expression, "this")
    2770        partition = self.partition_by_sql(expression)
    2771        order = expression.args.get("order")
    2772        order = self.order_sql(order, flat=True) if order else ""
    2773        spec = self.sql(expression, "spec")
    2774        alias = self.sql(expression, "alias")
    2775        over = self.sql(expression, "over") or "OVER"
    2776
    2777        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2778
    2779        first = expression.args.get("first")
    2780        if first is None:
    2781            first = ""
    2782        else:
    2783            first = "FIRST" if first else "LAST"
    2784
    2785        if not partition and not order and not spec and alias:
    2786            return f"{this} {alias}"
    2787
    2788        args = self.format_args(
    2789            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
    2790        )
    2791        return f"{this} ({args})"

def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str: View Source

    2793    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2794        partition = self.expressions(expression, key="partition_by", flat=True)
    2795        return f"PARTITION BY {partition}" if partition else ""

def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str: View Source

    2797    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2798        kind = self.sql(expression, "kind")
    2799        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2800        end = (
    2801            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2802            or "CURRENT ROW"
    2803        )
    2804        return f"{kind} BETWEEN {start} AND {end}"

def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str: View Source

    2806    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2807        this = self.sql(expression, "this")
    2808        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2809        return f"{this} WITHIN GROUP ({expression_sql})"

def between_sql(self, expression: sqlglot.expressions.Between) -> str: View Source

    2811    def between_sql(self, expression: exp.Between) -> str:
    2812        this = self.sql(expression, "this")
    2813        low = self.sql(expression, "low")
    2814        high = self.sql(expression, "high")
    2815        return f"{this} BETWEEN {low} AND {high}"

def bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket, index_offset: Optional[int] = None) -> List[sqlglot.expressions.Expression]: View Source

    2817    def bracket_offset_expressions(
    2818        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2819    ) -> t.List[exp.Expression]:
    2820        return apply_index_offset(
    2821            expression.this,
    2822            expression.expressions,
    2823            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2824            dialect=self.dialect,
    2825        )

def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str: View Source

    2827    def bracket_sql(self, expression: exp.Bracket) -> str:
    2828        expressions = self.bracket_offset_expressions(expression)
    2829        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2830        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

def all_sql(self, expression: sqlglot.expressions.All) -> str: View Source

    2832    def all_sql(self, expression: exp.All) -> str:
    2833        return f"ALL {self.wrap(expression)}"

def any_sql(self, expression: sqlglot.expressions.Any) -> str: View Source

    2835    def any_sql(self, expression: exp.Any) -> str:
    2836        this = self.sql(expression, "this")
    2837        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2838            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2839                this = self.wrap(this)
    2840            return f"ANY{this}"
    2841        return f"ANY {this}"

def exists_sql(self, expression: sqlglot.expressions.Exists) -> str: View Source

    2843    def exists_sql(self, expression: exp.Exists) -> str:
    2844        return f"EXISTS{self.wrap(expression)}"

def case_sql(self, expression: sqlglot.expressions.Case) -> str: View Source

    2846    def case_sql(self, expression: exp.Case) -> str:
    2847        this = self.sql(expression, "this")
    2848        statements = [f"CASE {this}" if this else "CASE"]
    2849
    2850        for e in expression.args["ifs"]:
    2851            statements.append(f"WHEN {self.sql(e, 'this')}")
    2852            statements.append(f"THEN {self.sql(e, 'true')}")
    2853
    2854        default = self.sql(expression, "default")
    2855
    2856        if default:
    2857            statements.append(f"ELSE {default}")
    2858
    2859        statements.append("END")
    2860
    2861        if self.pretty and self.too_wide(statements):
    2862            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2863
    2864        return " ".join(statements)

def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str: View Source

    2866    def constraint_sql(self, expression: exp.Constraint) -> str:
    2867        this = self.sql(expression, "this")
    2868        expressions = self.expressions(expression, flat=True)
    2869        return f"CONSTRAINT {this} {expressions}"

def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str: View Source

    2871    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2872        order = expression.args.get("order")
    2873        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2874        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"

def extract_sql(self, expression: sqlglot.expressions.Extract) -> str: View Source

    2876    def extract_sql(self, expression: exp.Extract) -> str:
    2877        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2878        expression_sql = self.sql(expression, "expression")
    2879        return f"EXTRACT({this} FROM {expression_sql})"

def trim_sql(self, expression: sqlglot.expressions.Trim) -> str: View Source

    2881    def trim_sql(self, expression: exp.Trim) -> str:
    2882        trim_type = self.sql(expression, "position")
    2883
    2884        if trim_type == "LEADING":
    2885            func_name = "LTRIM"
    2886        elif trim_type == "TRAILING":
    2887            func_name = "RTRIM"
    2888        else:
    2889            func_name = "TRIM"
    2890
    2891        return self.func(func_name, expression.this, expression.expression)

def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]: View Source

    2893    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2894        args = expression.expressions
    2895        if isinstance(expression, exp.ConcatWs):
    2896            args = args[1:]  # Skip the delimiter
    2897
    2898        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2899            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2900
    2901        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2902            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2903
    2904        return args

def concat_sql(self, expression: sqlglot.expressions.Concat) -> str: View Source

    2906    def concat_sql(self, expression: exp.Concat) -> str:
    2907        expressions = self.convert_concat_args(expression)
    2908
    2909        # Some dialects don't allow a single-argument CONCAT call
    2910        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2911            return self.sql(expressions[0])
    2912
    2913        return self.func("CONCAT", *expressions)

def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str: View Source

    2915    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2916        return self.func(
    2917            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2918        )

def check_sql(self, expression: sqlglot.expressions.Check) -> str: View Source

    2920    def check_sql(self, expression: exp.Check) -> str:
    2921        this = self.sql(expression, key="this")
    2922        return f"CHECK ({this})"

def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str: View Source

    2924    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2925        expressions = self.expressions(expression, flat=True)
    2926        expressions = f" ({expressions})" if expressions else ""
    2927        reference = self.sql(expression, "reference")
    2928        reference = f" {reference}" if reference else ""
    2929        delete = self.sql(expression, "delete")
    2930        delete = f" ON DELETE {delete}" if delete else ""
    2931        update = self.sql(expression, "update")
    2932        update = f" ON UPDATE {update}" if update else ""
    2933        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2934        options = f" {options}" if options else ""
    2935        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"

def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str: View Source

    2937    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2938        expressions = self.expressions(expression, flat=True)
    2939        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2940        options = f" {options}" if options else ""
    2941        return f"PRIMARY KEY ({expressions}){options}"

def if_sql(self, expression: sqlglot.expressions.If) -> str: View Source

    2943    def if_sql(self, expression: exp.If) -> str:
    2944        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))

def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str: View Source

    2946    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2947        modifier = expression.args.get("modifier")
    2948        modifier = f" {modifier}" if modifier else ""
    2949        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"

def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str: View Source

    2951    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2952        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"

def jsonpath_sql(self, expression: sqlglot.expressions.JSONPath) -> str: View Source

    2954    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2955        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2956
    2957        if expression.args.get("escape"):
    2958            path = self.escape_str(path)
    2959
    2960        if self.QUOTE_JSON_PATH:
    2961            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2962
    2963        return path

def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str: View Source

    2965    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2966        if isinstance(expression, exp.JSONPathPart):
    2967            transform = self.TRANSFORMS.get(expression.__class__)
    2968            if not callable(transform):
    2969                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2970                return ""
    2971
    2972            return transform(self, expression)
    2973
    2974        if isinstance(expression, int):
    2975            return str(expression)
    2976
    2977        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2978            escaped = expression.replace("'", "\\'")
    2979            escaped = f"\\'{expression}\\'"
    2980        else:
    2981            escaped = expression.replace('"', '\\"')
    2982            escaped = f'"{escaped}"'
    2983
    2984        return escaped

def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str: View Source

    2986    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2987        return f"{self.sql(expression, 'this')} FORMAT JSON"

def jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str: View Source

    2989    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2990        null_handling = expression.args.get("null_handling")
    2991        null_handling = f" {null_handling}" if null_handling else ""
    2992
    2993        unique_keys = expression.args.get("unique_keys")
    2994        if unique_keys is not None:
    2995            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2996        else:
    2997            unique_keys = ""
    2998
    2999        return_type = self.sql(expression, "return_type")
    3000        return_type = f" RETURNING {return_type}" if return_type else ""
    3001        encoding = self.sql(expression, "encoding")
    3002        encoding = f" ENCODING {encoding}" if encoding else ""
    3003
    3004        return self.func(
    3005            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    3006            *expression.expressions,
    3007            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    3008        )

def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str: View Source

    3010    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
    3011        return self.jsonobject_sql(expression)

def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str: View Source

    3013    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    3014        null_handling = expression.args.get("null_handling")
    3015        null_handling = f" {null_handling}" if null_handling else ""
    3016        return_type = self.sql(expression, "return_type")
    3017        return_type = f" RETURNING {return_type}" if return_type else ""
    3018        strict = " STRICT" if expression.args.get("strict") else ""
    3019        return self.func(
    3020            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    3021        )

def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str: View Source

    3023    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    3024        this = self.sql(expression, "this")
    3025        order = self.sql(expression, "order")
    3026        null_handling = expression.args.get("null_handling")
    3027        null_handling = f" {null_handling}" if null_handling else ""
    3028        return_type = self.sql(expression, "return_type")
    3029        return_type = f" RETURNING {return_type}" if return_type else ""
    3030        strict = " STRICT" if expression.args.get("strict") else ""
    3031        return self.func(
    3032            "JSON_ARRAYAGG",
    3033            this,
    3034            suffix=f"{order}{null_handling}{return_type}{strict})",
    3035        )

def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str: View Source

    3037    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    3038        path = self.sql(expression, "path")
    3039        path = f" PATH {path}" if path else ""
    3040        nested_schema = self.sql(expression, "nested_schema")
    3041
    3042        if nested_schema:
    3043            return f"NESTED{path} {nested_schema}"
    3044
    3045        this = self.sql(expression, "this")
    3046        kind = self.sql(expression, "kind")
    3047        kind = f" {kind}" if kind else ""
    3048        return f"{this}{kind}{path}"

def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str: View Source

    3050    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    3051        return self.func("COLUMNS", *expression.expressions)

def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str: View Source

    3053    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    3054        this = self.sql(expression, "this")
    3055        path = self.sql(expression, "path")
    3056        path = f", {path}" if path else ""
    3057        error_handling = expression.args.get("error_handling")
    3058        error_handling = f" {error_handling}" if error_handling else ""
    3059        empty_handling = expression.args.get("empty_handling")
    3060        empty_handling = f" {empty_handling}" if empty_handling else ""
    3061        schema = self.sql(expression, "schema")
    3062        return self.func(
    3063            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    3064        )

def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str: View Source

    3066    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    3067        this = self.sql(expression, "this")
    3068        kind = self.sql(expression, "kind")
    3069        path = self.sql(expression, "path")
    3070        path = f" {path}" if path else ""
    3071        as_json = " AS JSON" if expression.args.get("as_json") else ""
    3072        return f"{this} {kind}{path}{as_json}"

def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str: View Source

    3074    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    3075        this = self.sql(expression, "this")
    3076        path = self.sql(expression, "path")
    3077        path = f", {path}" if path else ""
    3078        expressions = self.expressions(expression)
    3079        with_ = (
    3080            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    3081            if expressions
    3082            else ""
    3083        )
    3084        return f"OPENJSON({this}{path}){with_}"

def in_sql(self, expression: sqlglot.expressions.In) -> str: View Source

    3086    def in_sql(self, expression: exp.In) -> str:
    3087        query = expression.args.get("query")
    3088        unnest = expression.args.get("unnest")
    3089        field = expression.args.get("field")
    3090        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3091
    3092        if query:
    3093            in_sql = self.sql(query)
    3094        elif unnest:
    3095            in_sql = self.in_unnest_op(unnest)
    3096        elif field:
    3097            in_sql = self.sql(field)
    3098        else:
    3099            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    3100
    3101        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str: View Source

    3103    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    3104        return f"(SELECT {self.sql(unnest)})"

def interval_sql(self, expression: sqlglot.expressions.Interval) -> str: View Source

    3106    def interval_sql(self, expression: exp.Interval) -> str:
    3107        unit = self.sql(expression, "unit")
    3108        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3109            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3110        unit = f" {unit}" if unit else ""
    3111
    3112        if self.SINGLE_STRING_INTERVAL:
    3113            this = expression.this.name if expression.this else ""
    3114            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    3115
    3116        this = self.sql(expression, "this")
    3117        if this:
    3118            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3119            this = f" {this}" if unwrapped else f" ({this})"
    3120
    3121        return f"INTERVAL{this}{unit}"

def return_sql(self, expression: sqlglot.expressions.Return) -> str: View Source

    3123    def return_sql(self, expression: exp.Return) -> str:
    3124        return f"RETURN {self.sql(expression, 'this')}"

def reference_sql(self, expression: sqlglot.expressions.Reference) -> str: View Source

    3126    def reference_sql(self, expression: exp.Reference) -> str:
    3127        this = self.sql(expression, "this")
    3128        expressions = self.expressions(expression, flat=True)
    3129        expressions = f"({expressions})" if expressions else ""
    3130        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3131        options = f" {options}" if options else ""
    3132        return f"REFERENCES {this}{expressions}{options}"

def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str: View Source

    3134    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3135        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3136        parent = expression.parent
    3137        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3138        return self.func(
    3139            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3140        )

def paren_sql(self, expression: sqlglot.expressions.Paren) -> str: View Source

    3142    def paren_sql(self, expression: exp.Paren) -> str:
    3143        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    3144        return f"({sql}{self.seg(')', sep='')}"

def neg_sql(self, expression: sqlglot.expressions.Neg) -> str: View Source

    3146    def neg_sql(self, expression: exp.Neg) -> str:
    3147        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    3148        this_sql = self.sql(expression, "this")
    3149        sep = " " if this_sql[0] == "-" else ""
    3150        return f"-{sep}{this_sql}"

def not_sql(self, expression: sqlglot.expressions.Not) -> str: View Source

    3152    def not_sql(self, expression: exp.Not) -> str:
    3153        return f"NOT {self.sql(expression, 'this')}"

def alias_sql(self, expression: sqlglot.expressions.Alias) -> str: View Source

    3155    def alias_sql(self, expression: exp.Alias) -> str:
    3156        alias = self.sql(expression, "alias")
    3157        alias = f" AS {alias}" if alias else ""
    3158        return f"{self.sql(expression, 'this')}{alias}"

def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str: View Source

    3160    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3161        alias = expression.args["alias"]
    3162
    3163        parent = expression.parent
    3164        pivot = parent and parent.parent
    3165
    3166        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
    3167            identifier_alias = isinstance(alias, exp.Identifier)
    3168            literal_alias = isinstance(alias, exp.Literal)
    3169
    3170            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3171                alias.replace(exp.Literal.string(alias.output_name))
    3172            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3173                alias.replace(exp.to_identifier(alias.output_name))
    3174
    3175        return self.alias_sql(expression)

def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str: View Source

    3177    def aliases_sql(self, expression: exp.Aliases) -> str:
    3178        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"

def atindex_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str: View Source

    3180    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3181        this = self.sql(expression, "this")
    3182        index = self.sql(expression, "expression")
    3183        return f"{this} AT {index}"

def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str: View Source

    3185    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3186        this = self.sql(expression, "this")
    3187        zone = self.sql(expression, "zone")
    3188        return f"{this} AT TIME ZONE {zone}"

def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str: View Source

    3190    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3191        this = self.sql(expression, "this")
    3192        zone = self.sql(expression, "zone")
    3193        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

def add_sql(self, expression: sqlglot.expressions.Add) -> str: View Source

    3195    def add_sql(self, expression: exp.Add) -> str:
    3196        return self.binary(expression, "+")

def and_sql( self, expression: sqlglot.expressions.And, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3198    def and_sql(
    3199        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3200    ) -> str:
    3201        return self.connector_sql(expression, "AND", stack)

def or_sql( self, expression: sqlglot.expressions.Or, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3203    def or_sql(
    3204        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3205    ) -> str:
    3206        return self.connector_sql(expression, "OR", stack)

def xor_sql( self, expression: sqlglot.expressions.Xor, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3208    def xor_sql(
    3209        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3210    ) -> str:
    3211        return self.connector_sql(expression, "XOR", stack)

def connector_sql( self, expression: sqlglot.expressions.Connector, op: str, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3213    def connector_sql(
    3214        self,
    3215        expression: exp.Connector,
    3216        op: str,
    3217        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3218    ) -> str:
    3219        if stack is not None:
    3220            if expression.expressions:
    3221                stack.append(self.expressions(expression, sep=f" {op} "))
    3222            else:
    3223                stack.append(expression.right)
    3224                if expression.comments and self.comments:
    3225                    for comment in expression.comments:
    3226                        if comment:
    3227                            op += f" /*{self.pad_comment(comment)}*/"
    3228                stack.extend((op, expression.left))
    3229            return op
    3230
    3231        stack = [expression]
    3232        sqls: t.List[str] = []
    3233        ops = set()
    3234
    3235        while stack:
    3236            node = stack.pop()
    3237            if isinstance(node, exp.Connector):
    3238                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3239            else:
    3240                sql = self.sql(node)
    3241                if sqls and sqls[-1] in ops:
    3242                    sqls[-1] += f" {sql}"
    3243                else:
    3244                    sqls.append(sql)
    3245
    3246        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3247        return sep.join(sqls)

def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str: View Source

    3249    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    3250        return self.binary(expression, "&")

def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str: View Source

    3252    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    3253        return self.binary(expression, "<<")

def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str: View Source

    3255    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    3256        return f"~{self.sql(expression, 'this')}"

def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str: View Source

    3258    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    3259        return self.binary(expression, "|")

def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str: View Source

    3261    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    3262        return self.binary(expression, ">>")

def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str: View Source

    3264    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    3265        return self.binary(expression, "^")

def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str: View Source

    3267    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3268        format_sql = self.sql(expression, "format")
    3269        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3270        to_sql = self.sql(expression, "to")
    3271        to_sql = f" {to_sql}" if to_sql else ""
    3272        action = self.sql(expression, "action")
    3273        action = f" {action}" if action else ""
    3274        default = self.sql(expression, "default")
    3275        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3276        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"

def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str: View Source

    3278    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    3279        zone = self.sql(expression, "this")
    3280        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"

def collate_sql(self, expression: sqlglot.expressions.Collate) -> str: View Source

    3282    def collate_sql(self, expression: exp.Collate) -> str:
    3283        if self.COLLATE_IS_FUNC:
    3284            return self.function_fallback_sql(expression)
    3285        return self.binary(expression, "COLLATE")

def command_sql(self, expression: sqlglot.expressions.Command) -> str: View Source

    3287    def command_sql(self, expression: exp.Command) -> str:
    3288        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"

def comment_sql(self, expression: sqlglot.expressions.Comment) -> str: View Source

    3290    def comment_sql(self, expression: exp.Comment) -> str:
    3291        this = self.sql(expression, "this")
    3292        kind = expression.args["kind"]
    3293        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3294        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3295        expression_sql = self.sql(expression, "expression")
    3296        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"

def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str: View Source

    3298    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3299        this = self.sql(expression, "this")
    3300        delete = " DELETE" if expression.args.get("delete") else ""
    3301        recompress = self.sql(expression, "recompress")
    3302        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3303        to_disk = self.sql(expression, "to_disk")
    3304        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3305        to_volume = self.sql(expression, "to_volume")
    3306        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3307        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str: View Source

    3309    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3310        where = self.sql(expression, "where")
    3311        group = self.sql(expression, "group")
    3312        aggregates = self.expressions(expression, key="aggregates")
    3313        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3314
    3315        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3316            return f"TTL {self.expressions(expression, flat=True)}"
    3317
    3318        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str: View Source

    3320    def transaction_sql(self, expression: exp.Transaction) -> str:
    3321        return "BEGIN"

def commit_sql(self, expression: sqlglot.expressions.Commit) -> str: View Source

    3323    def commit_sql(self, expression: exp.Commit) -> str:
    3324        chain = expression.args.get("chain")
    3325        if chain is not None:
    3326            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3327
    3328        return f"COMMIT{chain or ''}"

def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str: View Source

    3330    def rollback_sql(self, expression: exp.Rollback) -> str:
    3331        savepoint = expression.args.get("savepoint")
    3332        savepoint = f" TO {savepoint}" if savepoint else ""
    3333        return f"ROLLBACK{savepoint}"

def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str: View Source

    3335    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3336        this = self.sql(expression, "this")
    3337
    3338        dtype = self.sql(expression, "dtype")
    3339        if dtype:
    3340            collate = self.sql(expression, "collate")
    3341            collate = f" COLLATE {collate}" if collate else ""
    3342            using = self.sql(expression, "using")
    3343            using = f" USING {using}" if using else ""
    3344            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
    3345
    3346        default = self.sql(expression, "default")
    3347        if default:
    3348            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3349
    3350        comment = self.sql(expression, "comment")
    3351        if comment:
    3352            return f"ALTER COLUMN {this} COMMENT {comment}"
    3353
    3354        visible = expression.args.get("visible")
    3355        if visible:
    3356            return f"ALTER COLUMN {this} SET {visible}"
    3357
    3358        allow_null = expression.args.get("allow_null")
    3359        drop = expression.args.get("drop")
    3360
    3361        if not drop and not allow_null:
    3362            self.unsupported("Unsupported ALTER COLUMN syntax")
    3363
    3364        if allow_null is not None:
    3365            keyword = "DROP" if drop else "SET"
    3366            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3367
    3368        return f"ALTER COLUMN {this} DROP DEFAULT"

def alterindex_sql(self, expression: sqlglot.expressions.AlterIndex) -> str: View Source

    3370    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
    3371        this = self.sql(expression, "this")
    3372
    3373        visible = expression.args.get("visible")
    3374        visible_sql = "VISIBLE" if visible else "INVISIBLE"
    3375
    3376        return f"ALTER INDEX {this} {visible_sql}"

def alterdiststyle_sql(self, expression: sqlglot.expressions.AlterDistStyle) -> str: View Source

    3378    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3379        this = self.sql(expression, "this")
    3380        if not isinstance(expression.this, exp.Var):
    3381            this = f"KEY DISTKEY {this}"
    3382        return f"ALTER DISTSTYLE {this}"

def altersortkey_sql(self, expression: sqlglot.expressions.AlterSortKey) -> str: View Source

    3384    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3385        compound = " COMPOUND" if expression.args.get("compound") else ""
    3386        this = self.sql(expression, "this")
    3387        expressions = self.expressions(expression, flat=True)
    3388        expressions = f"({expressions})" if expressions else ""
    3389        return f"ALTER{compound} SORTKEY {this or expressions}"

def alterrename_sql(self, expression: sqlglot.expressions.AlterRename) -> str: View Source

    3391    def alterrename_sql(self, expression: exp.AlterRename) -> str:
    3392        if not self.RENAME_TABLE_WITH_DB:
    3393            # Remove db from tables
    3394            expression = expression.transform(
    3395                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3396            ).assert_is(exp.AlterRename)
    3397        this = self.sql(expression, "this")
    3398        return f"RENAME TO {this}"

def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str: View Source

    3400    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3401        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3402        old_column = self.sql(expression, "this")
    3403        new_column = self.sql(expression, "to")
    3404        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

def alterset_sql(self, expression: sqlglot.expressions.AlterSet) -> str: View Source

    3406    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3407        exprs = self.expressions(expression, flat=True)
    3408        return f"SET {exprs}"

def alter_sql(self, expression: sqlglot.expressions.Alter) -> str: View Source

    3410    def alter_sql(self, expression: exp.Alter) -> str:
    3411        actions = expression.args["actions"]
    3412
    3413        if isinstance(actions[0], exp.ColumnDef):
    3414            actions = self.add_column_sql(expression)
    3415        elif isinstance(actions[0], exp.Schema):
    3416            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3417        elif isinstance(actions[0], exp.Delete):
    3418            actions = self.expressions(expression, key="actions", flat=True)
    3419        elif isinstance(actions[0], exp.Query):
    3420            actions = "AS " + self.expressions(expression, key="actions")
    3421        else:
    3422            actions = self.expressions(expression, key="actions", flat=True)
    3423
    3424        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3425        on_cluster = self.sql(expression, "cluster")
    3426        on_cluster = f" {on_cluster}" if on_cluster else ""
    3427        only = " ONLY" if expression.args.get("only") else ""
    3428        options = self.expressions(expression, key="options")
    3429        options = f", {options}" if options else ""
    3430        kind = self.sql(expression, "kind")
    3431        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3432
    3433        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"

def add_column_sql(self, expression: sqlglot.expressions.Alter) -> str: View Source

    3435    def add_column_sql(self, expression: exp.Alter) -> str:
    3436        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3437            return self.expressions(
    3438                expression,
    3439                key="actions",
    3440                prefix="ADD COLUMN ",
    3441                skip_first=True,
    3442            )
    3443        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str: View Source

    3445    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3446        expressions = self.expressions(expression)
    3447        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3448        return f"DROP{exists}{expressions}"

def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str: View Source

    3450    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3451        return f"ADD {self.expressions(expression)}"

def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str: View Source

    3453    def distinct_sql(self, expression: exp.Distinct) -> str:
    3454        this = self.expressions(expression, flat=True)
    3455
    3456        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3457            case = exp.case()
    3458            for arg in expression.expressions:
    3459                case = case.when(arg.is_(exp.null()), exp.null())
    3460            this = self.sql(case.else_(f"({this})"))
    3461
    3462        this = f" {this}" if this else ""
    3463
    3464        on = self.sql(expression, "on")
    3465        on = f" ON {on}" if on else ""
    3466        return f"DISTINCT{this}{on}"

def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str: View Source

    3468    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    3469        return self._embed_ignore_nulls(expression, "IGNORE NULLS")

def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str: View Source

    3471    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    3472        return self._embed_ignore_nulls(expression, "RESPECT NULLS")

def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str: View Source

    3474    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3475        this_sql = self.sql(expression, "this")
    3476        expression_sql = self.sql(expression, "expression")
    3477        kind = "MAX" if expression.args.get("max") else "MIN"
    3478        return f"{this_sql} HAVING {kind} {expression_sql}"

def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str: View Source

    3480    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3481        return self.sql(
    3482            exp.Cast(
    3483                this=exp.Div(this=expression.this, expression=expression.expression),
    3484                to=exp.DataType(this=exp.DataType.Type.INT),
    3485            )
    3486        )

def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str: View Source

    3488    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3489        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3490            return self.func(
    3491                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3492            )
    3493        return self.binary(expression, "||")

def div_sql(self, expression: sqlglot.expressions.Div) -> str: View Source

    3495    def div_sql(self, expression: exp.Div) -> str:
    3496        l, r = expression.left, expression.right
    3497
    3498        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3499            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3500
    3501        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3502            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3503                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3504
    3505        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3506            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3507                return self.sql(
    3508                    exp.cast(
    3509                        l / r,
    3510                        to=exp.DataType.Type.BIGINT,
    3511                    )
    3512                )
    3513
    3514        return self.binary(expression, "/")

def safedivide_sql(self, expression: sqlglot.expressions.SafeDivide) -> str: View Source

    3516    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3517        n = exp._wrap(expression.this, exp.Binary)
    3518        d = exp._wrap(expression.expression, exp.Binary)
    3519        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))

def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str: View Source

    3521    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    3522        return self.binary(expression, "OVERLAPS")

def distance_sql(self, expression: sqlglot.expressions.Distance) -> str: View Source

    3524    def distance_sql(self, expression: exp.Distance) -> str:
    3525        return self.binary(expression, "<->")

def dot_sql(self, expression: sqlglot.expressions.Dot) -> str: View Source

    3527    def dot_sql(self, expression: exp.Dot) -> str:
    3528        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"

def eq_sql(self, expression: sqlglot.expressions.EQ) -> str: View Source

    3530    def eq_sql(self, expression: exp.EQ) -> str:
    3531        return self.binary(expression, "=")

def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str: View Source

    3533    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    3534        return self.binary(expression, ":=")

def escape_sql(self, expression: sqlglot.expressions.Escape) -> str: View Source

    3536    def escape_sql(self, expression: exp.Escape) -> str:
    3537        return self.binary(expression, "ESCAPE")

def glob_sql(self, expression: sqlglot.expressions.Glob) -> str: View Source

    3539    def glob_sql(self, expression: exp.Glob) -> str:
    3540        return self.binary(expression, "GLOB")

def gt_sql(self, expression: sqlglot.expressions.GT) -> str: View Source

    3542    def gt_sql(self, expression: exp.GT) -> str:
    3543        return self.binary(expression, ">")

def gte_sql(self, expression: sqlglot.expressions.GTE) -> str: View Source

    3545    def gte_sql(self, expression: exp.GTE) -> str:
    3546        return self.binary(expression, ">=")

def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str: View Source

    3548    def ilike_sql(self, expression: exp.ILike) -> str:
    3549        return self.binary(expression, "ILIKE")

def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str: View Source

    3551    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    3552        return self.binary(expression, "ILIKE ANY")

def is_sql(self, expression: sqlglot.expressions.Is) -> str: View Source

    3554    def is_sql(self, expression: exp.Is) -> str:
    3555        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3556            return self.sql(
    3557                expression.this if expression.expression.this else exp.not_(expression.this)
    3558            )
    3559        return self.binary(expression, "IS")

def like_sql(self, expression: sqlglot.expressions.Like) -> str: View Source

    3561    def like_sql(self, expression: exp.Like) -> str:
    3562        return self.binary(expression, "LIKE")

def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str: View Source

    3564    def likeany_sql(self, expression: exp.LikeAny) -> str:
    3565        return self.binary(expression, "LIKE ANY")

def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str: View Source

    3567    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    3568        return self.binary(expression, "SIMILAR TO")

def lt_sql(self, expression: sqlglot.expressions.LT) -> str: View Source

    3570    def lt_sql(self, expression: exp.LT) -> str:
    3571        return self.binary(expression, "<")

def lte_sql(self, expression: sqlglot.expressions.LTE) -> str: View Source

    3573    def lte_sql(self, expression: exp.LTE) -> str:
    3574        return self.binary(expression, "<=")

def mod_sql(self, expression: sqlglot.expressions.Mod) -> str: View Source

    3576    def mod_sql(self, expression: exp.Mod) -> str:
    3577        return self.binary(expression, "%")

def mul_sql(self, expression: sqlglot.expressions.Mul) -> str: View Source

    3579    def mul_sql(self, expression: exp.Mul) -> str:
    3580        return self.binary(expression, "*")

def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str: View Source

    3582    def neq_sql(self, expression: exp.NEQ) -> str:
    3583        return self.binary(expression, "<>")

def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str: View Source

    3585    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    3586        return self.binary(expression, "IS NOT DISTINCT FROM")

def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str: View Source

    3588    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    3589        return self.binary(expression, "IS DISTINCT FROM")

def slice_sql(self, expression: sqlglot.expressions.Slice) -> str: View Source

    3591    def slice_sql(self, expression: exp.Slice) -> str:
    3592        return self.binary(expression, ":")

def sub_sql(self, expression: sqlglot.expressions.Sub) -> str: View Source

    3594    def sub_sql(self, expression: exp.Sub) -> str:
    3595        return self.binary(expression, "-")

def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str: View Source

    3597    def trycast_sql(self, expression: exp.TryCast) -> str:
    3598        return self.cast_sql(expression, safe_prefix="TRY_")

def jsoncast_sql(self, expression: sqlglot.expressions.JSONCast) -> str: View Source

    3600    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
    3601        return self.cast_sql(expression)

def try_sql(self, expression: sqlglot.expressions.Try) -> str: View Source

    3603    def try_sql(self, expression: exp.Try) -> str:
    3604        if not self.TRY_SUPPORTED:
    3605            self.unsupported("Unsupported TRY function")
    3606            return self.sql(expression, "this")
    3607
    3608        return self.func("TRY", expression.this)

def log_sql(self, expression: sqlglot.expressions.Log) -> str: View Source

    3610    def log_sql(self, expression: exp.Log) -> str:
    3611        this = expression.this
    3612        expr = expression.expression
    3613
    3614        if self.dialect.LOG_BASE_FIRST is False:
    3615            this, expr = expr, this
    3616        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3617            if this.name in ("2", "10"):
    3618                return self.func(f"LOG{this.name}", expr)
    3619
    3620            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3621
    3622        return self.func("LOG", this, expr)

def use_sql(self, expression: sqlglot.expressions.Use) -> str: View Source

    3624    def use_sql(self, expression: exp.Use) -> str:
    3625        kind = self.sql(expression, "kind")
    3626        kind = f" {kind}" if kind else ""
    3627        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
    3628        this = f" {this}" if this else ""
    3629        return f"USE{kind}{this}"

def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str: View Source

    3631    def binary(self, expression: exp.Binary, op: str) -> str:
    3632        sqls: t.List[str] = []
    3633        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3634        binary_type = type(expression)
    3635
    3636        while stack:
    3637            node = stack.pop()
    3638
    3639            if type(node) is binary_type:
    3640                op_func = node.args.get("operator")
    3641                if op_func:
    3642                    op = f"OPERATOR({self.sql(op_func)})"
    3643
    3644                stack.append(node.right)
    3645                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3646                stack.append(node.left)
    3647            else:
    3648                sqls.append(self.sql(node))
    3649
    3650        return "".join(sqls)

def ceil_floor( self, expression: sqlglot.expressions.Ceil | sqlglot.expressions.Floor) -> str: View Source

    3652    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3653        to_clause = self.sql(expression, "to")
    3654        if to_clause:
    3655            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3656
    3657        return self.function_fallback_sql(expression)

def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str: View Source

    3659    def function_fallback_sql(self, expression: exp.Func) -> str:
    3660        args = []
    3661
    3662        for key in expression.arg_types:
    3663            arg_value = expression.args.get(key)
    3664
    3665            if isinstance(arg_value, list):
    3666                for value in arg_value:
    3667                    args.append(value)
    3668            elif arg_value is not None:
    3669                args.append(arg_value)
    3670
    3671        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3672            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3673        else:
    3674            name = expression.sql_name()
    3675
    3676        return self.func(name, *args)

def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')', normalize: bool = True) -> str: View Source

    3678    def func(
    3679        self,
    3680        name: str,
    3681        *args: t.Optional[exp.Expression | str],
    3682        prefix: str = "(",
    3683        suffix: str = ")",
    3684        normalize: bool = True,
    3685    ) -> str:
    3686        name = self.normalize_func(name) if normalize else name
    3687        return f"{name}{prefix}{self.format_args(*args)}{suffix}"

def format_args( self, *args: Union[str, sqlglot.expressions.Expression, NoneType], sep: str = ', ') -> str: View Source

    3689    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3690        arg_sqls = tuple(
    3691            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3692        )
    3693        if self.pretty and self.too_wide(arg_sqls):
    3694            return self.indent(
    3695                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3696            )
    3697        return sep.join(arg_sqls)

def too_wide(self, args: Iterable) -> bool: View Source

    3699    def too_wide(self, args: t.Iterable) -> bool:
    3700        return sum(len(arg) for arg in args) > self.max_text_width

def format_time( self, expression: sqlglot.expressions.Expression, inverse_time_mapping: Optional[Dict[str, str]] = None, inverse_time_trie: Optional[Dict] = None) -> Optional[str]: View Source

    3702    def format_time(
    3703        self,
    3704        expression: exp.Expression,
    3705        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3706        inverse_time_trie: t.Optional[t.Dict] = None,
    3707    ) -> t.Optional[str]:
    3708        return format_time(
    3709            self.sql(expression, "format"),
    3710            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3711            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3712        )

def expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[Collection[Union[str, sqlglot.expressions.Expression]]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, skip_last: bool = False, sep: str = ', ', prefix: str = '', dynamic: bool = False, new_line: bool = False) -> str: View Source

    3714    def expressions(
    3715        self,
    3716        expression: t.Optional[exp.Expression] = None,
    3717        key: t.Optional[str] = None,
    3718        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3719        flat: bool = False,
    3720        indent: bool = True,
    3721        skip_first: bool = False,
    3722        skip_last: bool = False,
    3723        sep: str = ", ",
    3724        prefix: str = "",
    3725        dynamic: bool = False,
    3726        new_line: bool = False,
    3727    ) -> str:
    3728        expressions = expression.args.get(key or "expressions") if expression else sqls
    3729
    3730        if not expressions:
    3731            return ""
    3732
    3733        if flat:
    3734            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3735
    3736        num_sqls = len(expressions)
    3737        result_sqls = []
    3738
    3739        for i, e in enumerate(expressions):
    3740            sql = self.sql(e, comment=False)
    3741            if not sql:
    3742                continue
    3743
    3744            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3745
    3746            if self.pretty:
    3747                if self.leading_comma:
    3748                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3749                else:
    3750                    result_sqls.append(
    3751                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3752                    )
    3753            else:
    3754                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3755
    3756        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3757            if new_line:
    3758                result_sqls.insert(0, "")
    3759                result_sqls.append("")
    3760            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3761        else:
    3762            result_sql = "".join(result_sqls)
    3763
    3764        return (
    3765            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3766            if indent
    3767            else result_sql
    3768        )

def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str: View Source

    3770    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3771        flat = flat or isinstance(expression.parent, exp.Properties)
    3772        expressions_sql = self.expressions(expression, flat=flat)
    3773        if flat:
    3774            return f"{op} {expressions_sql}"
    3775        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

def naked_property(self, expression: sqlglot.expressions.Property) -> str: View Source

    3777    def naked_property(self, expression: exp.Property) -> str:
    3778        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3779        if not property_name:
    3780            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3781        return f"{property_name} {self.sql(expression, 'this')}"

def tag_sql(self, expression: sqlglot.expressions.Tag) -> str: View Source

    3783    def tag_sql(self, expression: exp.Tag) -> str:
    3784        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"

def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str: View Source

    3786    def token_sql(self, token_type: TokenType) -> str:
    3787        return self.TOKEN_MAPPING.get(token_type, token_type.name)

def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str: View Source

    3789    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3790        this = self.sql(expression, "this")
    3791        expressions = self.no_identify(self.expressions, expression)
    3792        expressions = (
    3793            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3794        )
    3795        return f"{this}{expressions}" if expressions.strip() != "" else this

def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str: View Source

    3797    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3798        this = self.sql(expression, "this")
    3799        expressions = self.expressions(expression, flat=True)
    3800        return f"{this}({expressions})"

def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str: View Source

    3802    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    3803        return self.binary(expression, "=>")

def when_sql(self, expression: sqlglot.expressions.When) -> str: View Source

    3805    def when_sql(self, expression: exp.When) -> str:
    3806        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3807        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3808        condition = self.sql(expression, "condition")
    3809        condition = f" AND {condition}" if condition else ""
    3810
    3811        then_expression = expression.args.get("then")
    3812        if isinstance(then_expression, exp.Insert):
    3813            this = self.sql(then_expression, "this")
    3814            this = f"INSERT {this}" if this else "INSERT"
    3815            then = self.sql(then_expression, "expression")
    3816            then = f"{this} VALUES {then}" if then else this
    3817        elif isinstance(then_expression, exp.Update):
    3818            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3819                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3820            else:
    3821                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3822        else:
    3823            then = self.sql(then_expression)
    3824        return f"WHEN {matched}{source}{condition} THEN {then}"

def whens_sql(self, expression: sqlglot.expressions.Whens) -> str: View Source

    3826    def whens_sql(self, expression: exp.Whens) -> str:
    3827        return self.expressions(expression, sep=" ", indent=False)

def merge_sql(self, expression: sqlglot.expressions.Merge) -> str: View Source

    3829    def merge_sql(self, expression: exp.Merge) -> str:
    3830        table = expression.this
    3831        table_alias = ""
    3832
    3833        hints = table.args.get("hints")
    3834        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3835            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3836            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3837
    3838        this = self.sql(table)
    3839        using = f"USING {self.sql(expression, 'using')}"
    3840        on = f"ON {self.sql(expression, 'on')}"
    3841        whens = self.sql(expression, "whens")
    3842
    3843        returning = self.sql(expression, "returning")
    3844        if returning:
    3845            whens = f"{whens}{returning}"
    3846
    3847        sep = self.sep()
    3848
    3849        return self.prepend_ctes(
    3850            expression,
    3851            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    3852        )

@unsupported_args('format')
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str: View Source

    3854    @unsupported_args("format")
    3855    def tochar_sql(self, expression: exp.ToChar) -> str:
    3856        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))

def tonumber_sql(self, expression: sqlglot.expressions.ToNumber) -> str: View Source

    3858    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3859        if not self.SUPPORTS_TO_NUMBER:
    3860            self.unsupported("Unsupported TO_NUMBER function")
    3861            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3862
    3863        fmt = expression.args.get("format")
    3864        if not fmt:
    3865            self.unsupported("Conversion format is required for TO_NUMBER")
    3866            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3867
    3868        return self.func("TO_NUMBER", expression.this, fmt)

def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str: View Source

    3870    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3871        this = self.sql(expression, "this")
    3872        kind = self.sql(expression, "kind")
    3873        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3874        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3875        return f"{this}({kind}{args})"

def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str: View Source

    3877    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3878        this = self.sql(expression, "this")
    3879        max = self.sql(expression, "max")
    3880        min = self.sql(expression, "min")
    3881        return f"{this}(MIN {min} MAX {max})"

def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str: View Source

    3883    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    3884        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"

def duplicatekeyproperty_sql(self, expression: sqlglot.expressions.DuplicateKeyProperty) -> str: View Source

    3886    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
    3887        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"

def uniquekeyproperty_sql(self, expression: sqlglot.expressions.UniqueKeyProperty) -> str: View Source

    3890    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
    3891        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"

def distributedbyproperty_sql(self, expression: sqlglot.expressions.DistributedByProperty) -> str: View Source

    3894    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    3895        expressions = self.expressions(expression, flat=True)
    3896        expressions = f" {self.wrap(expressions)}" if expressions else ""
    3897        buckets = self.sql(expression, "buckets")
    3898        kind = self.sql(expression, "kind")
    3899        buckets = f" BUCKETS {buckets}" if buckets else ""
    3900        order = self.sql(expression, "order")
    3901        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"

def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str: View Source

    3903    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    3904        return ""

def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str: View Source

    3906    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3907        expressions = self.expressions(expression, key="expressions", flat=True)
    3908        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3909        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3910        buckets = self.sql(expression, "buckets")
    3911        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str: View Source

    3913    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3914        this = self.sql(expression, "this")
    3915        having = self.sql(expression, "having")
    3916
    3917        if having:
    3918            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3919
    3920        return self.func("ANY_VALUE", this)

def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str: View Source

    3922    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3923        transform = self.func("TRANSFORM", *expression.expressions)
    3924        row_format_before = self.sql(expression, "row_format_before")
    3925        row_format_before = f" {row_format_before}" if row_format_before else ""
    3926        record_writer = self.sql(expression, "record_writer")
    3927        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3928        using = f" USING {self.sql(expression, 'command_script')}"
    3929        schema = self.sql(expression, "schema")
    3930        schema = f" AS {schema}" if schema else ""
    3931        row_format_after = self.sql(expression, "row_format_after")
    3932        row_format_after = f" {row_format_after}" if row_format_after else ""
    3933        record_reader = self.sql(expression, "record_reader")
    3934        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3935        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"

def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str: View Source

    3937    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3938        key_block_size = self.sql(expression, "key_block_size")
    3939        if key_block_size:
    3940            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3941
    3942        using = self.sql(expression, "using")
    3943        if using:
    3944            return f"USING {using}"
    3945
    3946        parser = self.sql(expression, "parser")
    3947        if parser:
    3948            return f"WITH PARSER {parser}"
    3949
    3950        comment = self.sql(expression, "comment")
    3951        if comment:
    3952            return f"COMMENT {comment}"
    3953
    3954        visible = expression.args.get("visible")
    3955        if visible is not None:
    3956            return "VISIBLE" if visible else "INVISIBLE"
    3957
    3958        engine_attr = self.sql(expression, "engine_attr")
    3959        if engine_attr:
    3960            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3961
    3962        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3963        if secondary_engine_attr:
    3964            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3965
    3966        self.unsupported("Unsupported index constraint option.")
    3967        return ""

def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str: View Source

    3969    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
    3970        enforced = " ENFORCED" if expression.args.get("enforced") else ""
    3971        return f"CHECK ({self.sql(expression, 'this')}){enforced}"

def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str: View Source

    3973    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3974        kind = self.sql(expression, "kind")
    3975        kind = f"{kind} INDEX" if kind else "INDEX"
    3976        this = self.sql(expression, "this")
    3977        this = f" {this}" if this else ""
    3978        index_type = self.sql(expression, "index_type")
    3979        index_type = f" USING {index_type}" if index_type else ""
    3980        expressions = self.expressions(expression, flat=True)
    3981        expressions = f" ({expressions})" if expressions else ""
    3982        options = self.expressions(expression, key="options", sep=" ")
    3983        options = f" {options}" if options else ""
    3984        return f"{kind}{this}{index_type}{expressions}{options}"

def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str: View Source

    3986    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3987        if self.NVL2_SUPPORTED:
    3988            return self.function_fallback_sql(expression)
    3989
    3990        case = exp.Case().when(
    3991            expression.this.is_(exp.null()).not_(copy=False),
    3992            expression.args["true"],
    3993            copy=False,
    3994        )
    3995        else_cond = expression.args.get("false")
    3996        if else_cond:
    3997            case.else_(else_cond, copy=False)
    3998
    3999        return self.sql(case)

def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str: View Source

    4001    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    4002        this = self.sql(expression, "this")
    4003        expr = self.sql(expression, "expression")
    4004        iterator = self.sql(expression, "iterator")
    4005        condition = self.sql(expression, "condition")
    4006        condition = f" IF {condition}" if condition else ""
    4007        return f"{this} FOR {expr} IN {iterator}{condition}"

def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str: View Source

    4009    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    4010        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"

def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str: View Source

    4012    def opclass_sql(self, expression: exp.Opclass) -> str:
    4013        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"

def predict_sql(self, expression: sqlglot.expressions.Predict) -> str: View Source

    4015    def predict_sql(self, expression: exp.Predict) -> str:
    4016        model = self.sql(expression, "this")
    4017        model = f"MODEL {model}"
    4018        table = self.sql(expression, "expression")
    4019        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    4020        parameters = self.sql(expression, "params_struct")
    4021        return self.func("PREDICT", model, table, parameters or None)

def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str: View Source

    4023    def forin_sql(self, expression: exp.ForIn) -> str:
    4024        this = self.sql(expression, "this")
    4025        expression_sql = self.sql(expression, "expression")
    4026        return f"FOR {this} DO {expression_sql}"

def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str: View Source

    4028    def refresh_sql(self, expression: exp.Refresh) -> str:
    4029        this = self.sql(expression, "this")
    4030        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    4031        return f"REFRESH {table}{this}"

def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str: View Source

    4033    def toarray_sql(self, expression: exp.ToArray) -> str:
    4034        arg = expression.this
    4035        if not arg.type:
    4036            from sqlglot.optimizer.annotate_types import annotate_types
    4037
    4038            arg = annotate_types(arg, dialect=self.dialect)
    4039
    4040        if arg.is_type(exp.DataType.Type.ARRAY):
    4041            return self.sql(arg)
    4042
    4043        cond_for_null = arg.is_(exp.null())
    4044        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))

def tsordstotime_sql(self, expression: sqlglot.expressions.TsOrDsToTime) -> str: View Source

    4046    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    4047        this = expression.this
    4048        time_format = self.format_time(expression)
    4049
    4050        if time_format:
    4051            return self.sql(
    4052                exp.cast(
    4053                    exp.StrToTime(this=this, format=expression.args["format"]),
    4054                    exp.DataType.Type.TIME,
    4055                )
    4056            )
    4057
    4058        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    4059            return self.sql(this)
    4060
    4061        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

def tsordstotimestamp_sql(self, expression: sqlglot.expressions.TsOrDsToTimestamp) -> str: View Source

    4063    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    4064        this = expression.this
    4065        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    4066            return self.sql(this)
    4067
    4068        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))

def tsordstodatetime_sql(self, expression: sqlglot.expressions.TsOrDsToDatetime) -> str: View Source

    4070    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    4071        this = expression.this
    4072        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    4073            return self.sql(this)
    4074
    4075        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))

def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str: View Source

    4077    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    4078        this = expression.this
    4079        time_format = self.format_time(expression)
    4080
    4081        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    4082            return self.sql(
    4083                exp.cast(
    4084                    exp.StrToTime(this=this, format=expression.args["format"]),
    4085                    exp.DataType.Type.DATE,
    4086                )
    4087            )
    4088
    4089        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    4090            return self.sql(this)
    4091
    4092        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str: View Source

    4094    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    4095        return self.sql(
    4096            exp.func(
    4097                "DATEDIFF",
    4098                expression.this,
    4099                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    4100                "day",
    4101            )
    4102        )

def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str: View Source

    4104    def lastday_sql(self, expression: exp.LastDay) -> str:
    4105        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4106            return self.function_fallback_sql(expression)
    4107
    4108        unit = expression.text("unit")
    4109        if unit and unit != "MONTH":
    4110            self.unsupported("Date parts are not supported in LAST_DAY.")
    4111
    4112        return self.func("LAST_DAY", expression.this)

def dateadd_sql(self, expression: sqlglot.expressions.DateAdd) -> str: View Source

    4114    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4115        from sqlglot.dialects.dialect import unit_to_str
    4116
    4117        return self.func(
    4118            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4119        )

def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str: View Source

    4121    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4122        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4123            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4124            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4125            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4126            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4127
    4128        from sqlglot.dialects import Dialect
    4129
    4130        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4131        if self.dialect.__class__ != Dialect:
    4132            self.unsupported("ARRAY_ANY is unsupported")
    4133
    4134        return self.function_fallback_sql(expression)

def struct_sql(self, expression: sqlglot.expressions.Struct) -> str: View Source

    4136    def struct_sql(self, expression: exp.Struct) -> str:
    4137        expression.set(
    4138            "expressions",
    4139            [
    4140                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4141                if isinstance(e, exp.PropertyEQ)
    4142                else e
    4143                for e in expression.expressions
    4144            ],
    4145        )
    4146
    4147        return self.function_fallback_sql(expression)

def partitionrange_sql(self, expression: sqlglot.expressions.PartitionRange) -> str: View Source

    4149    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4150        low = self.sql(expression, "this")
    4151        high = self.sql(expression, "expression")
    4152
    4153        return f"{low} TO {high}"

def truncatetable_sql(self, expression: sqlglot.expressions.TruncateTable) -> str: View Source

    4155    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4156        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4157        tables = f" {self.expressions(expression)}"
    4158
    4159        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4160
    4161        on_cluster = self.sql(expression, "cluster")
    4162        on_cluster = f" {on_cluster}" if on_cluster else ""
    4163
    4164        identity = self.sql(expression, "identity")
    4165        identity = f" {identity} IDENTITY" if identity else ""
    4166
    4167        option = self.sql(expression, "option")
    4168        option = f" {option}" if option else ""
    4169
    4170        partition = self.sql(expression, "partition")
    4171        partition = f" {partition}" if partition else ""
    4172
    4173        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

def convert_sql(self, expression: sqlglot.expressions.Convert) -> str: View Source

    4177    def convert_sql(self, expression: exp.Convert) -> str:
    4178        to = expression.this
    4179        value = expression.expression
    4180        style = expression.args.get("style")
    4181        safe = expression.args.get("safe")
    4182        strict = expression.args.get("strict")
    4183
    4184        if not to or not value:
    4185            return ""
    4186
    4187        # Retrieve length of datatype and override to default if not specified
    4188        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4189            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4190
    4191        transformed: t.Optional[exp.Expression] = None
    4192        cast = exp.Cast if strict else exp.TryCast
    4193
    4194        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4195        if isinstance(style, exp.Literal) and style.is_int:
    4196            from sqlglot.dialects.tsql import TSQL
    4197
    4198            style_value = style.name
    4199            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4200            if not converted_style:
    4201                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4202
    4203            fmt = exp.Literal.string(converted_style)
    4204
    4205            if to.this == exp.DataType.Type.DATE:
    4206                transformed = exp.StrToDate(this=value, format=fmt)
    4207            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4208                transformed = exp.StrToTime(this=value, format=fmt)
    4209            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4210                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4211            elif to.this == exp.DataType.Type.TEXT:
    4212                transformed = exp.TimeToStr(this=value, format=fmt)
    4213
    4214        if not transformed:
    4215            transformed = cast(this=value, to=to, safe=safe)
    4216
    4217        return self.sql(transformed)

def copyparameter_sql(self, expression: sqlglot.expressions.CopyParameter) -> str: View Source

    4277    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4278        option = self.sql(expression, "this")
    4279
    4280        if expression.expressions:
    4281            upper = option.upper()
    4282
    4283            # Snowflake FILE_FORMAT options are separated by whitespace
    4284            sep = " " if upper == "FILE_FORMAT" else ", "
    4285
    4286            # Databricks copy/format options do not set their list of values with EQ
    4287            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4288            values = self.expressions(expression, flat=True, sep=sep)
    4289            return f"{option}{op}({values})"
    4290
    4291        value = self.sql(expression, "expression")
    4292
    4293        if not value:
    4294            return option
    4295
    4296        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4297
    4298        return f"{option}{op}{value}"

def credentials_sql(self, expression: sqlglot.expressions.Credentials) -> str: View Source

    4300    def credentials_sql(self, expression: exp.Credentials) -> str:
    4301        cred_expr = expression.args.get("credentials")
    4302        if isinstance(cred_expr, exp.Literal):
    4303            # Redshift case: CREDENTIALS <string>
    4304            credentials = self.sql(expression, "credentials")
    4305            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4306        else:
    4307            # Snowflake case: CREDENTIALS = (...)
    4308            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4309            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4310
    4311        storage = self.sql(expression, "storage")
    4312        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4313
    4314        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4315        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4316
    4317        iam_role = self.sql(expression, "iam_role")
    4318        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4319
    4320        region = self.sql(expression, "region")
    4321        region = f" REGION {region}" if region else ""
    4322
    4323        return f"{credentials}{storage}{encryption}{iam_role}{region}"

def copy_sql(self, expression: sqlglot.expressions.Copy) -> str: View Source

    4325    def copy_sql(self, expression: exp.Copy) -> str:
    4326        this = self.sql(expression, "this")
    4327        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4328
    4329        credentials = self.sql(expression, "credentials")
    4330        credentials = self.seg(credentials) if credentials else ""
    4331        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    4332        files = self.expressions(expression, key="files", flat=True)
    4333
    4334        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4335        params = self.expressions(
    4336            expression,
    4337            key="params",
    4338            sep=sep,
    4339            new_line=True,
    4340            skip_last=True,
    4341            skip_first=True,
    4342            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4343        )
    4344
    4345        if params:
    4346            if self.COPY_PARAMS_ARE_WRAPPED:
    4347                params = f" WITH ({params})"
    4348            elif not self.pretty:
    4349                params = f" {params}"
    4350
    4351        return f"COPY{this}{kind} {files}{credentials}{params}"

def semicolon_sql(self, expression: sqlglot.expressions.Semicolon) -> str: View Source

    4353    def semicolon_sql(self, expression: exp.Semicolon) -> str:
    4354        return ""

def datadeletionproperty_sql(self, expression: sqlglot.expressions.DataDeletionProperty) -> str: View Source

    4356    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4357        on_sql = "ON" if expression.args.get("on") else "OFF"
    4358        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4359        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4360        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4361        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4362
    4363        if filter_col or retention_period:
    4364            on_sql = self.func("ON", filter_col, retention_period)
    4365
    4366        return f"DATA_DELETION={on_sql}"

def maskingpolicycolumnconstraint_sql( self, expression: sqlglot.expressions.MaskingPolicyColumnConstraint) -> str: View Source

    4368    def maskingpolicycolumnconstraint_sql(
    4369        self, expression: exp.MaskingPolicyColumnConstraint
    4370    ) -> str:
    4371        this = self.sql(expression, "this")
    4372        expressions = self.expressions(expression, flat=True)
    4373        expressions = f" USING ({expressions})" if expressions else ""
    4374        return f"MASKING POLICY {this}{expressions}"

def gapfill_sql(self, expression: sqlglot.expressions.GapFill) -> str: View Source

    4376    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4377        this = self.sql(expression, "this")
    4378        this = f"TABLE {this}"
    4379        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])

def scope_resolution(self, rhs: str, scope_name: str) -> str: View Source

    4381    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    4382        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)

def scoperesolution_sql(self, expression: sqlglot.expressions.ScopeResolution) -> str: View Source

    4384    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4385        this = self.sql(expression, "this")
    4386        expr = expression.expression
    4387
    4388        if isinstance(expr, exp.Func):
    4389            # T-SQL's CLR functions are case sensitive
    4390            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4391        else:
    4392            expr = self.sql(expression, "expression")
    4393
    4394        return self.scope_resolution(expr, this)

def parsejson_sql(self, expression: sqlglot.expressions.ParseJSON) -> str: View Source

    4396    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4397        if self.PARSE_JSON_NAME is None:
    4398            return self.sql(expression.this)
    4399
    4400        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)

def rand_sql(self, expression: sqlglot.expressions.Rand) -> str: View Source

    4402    def rand_sql(self, expression: exp.Rand) -> str:
    4403        lower = self.sql(expression, "lower")
    4404        upper = self.sql(expression, "upper")
    4405
    4406        if lower and upper:
    4407            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4408        return self.func("RAND", expression.this)

def changes_sql(self, expression: sqlglot.expressions.Changes) -> str: View Source

    4410    def changes_sql(self, expression: exp.Changes) -> str:
    4411        information = self.sql(expression, "information")
    4412        information = f"INFORMATION => {information}"
    4413        at_before = self.sql(expression, "at_before")
    4414        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4415        end = self.sql(expression, "end")
    4416        end = f"{self.seg('')}{end}" if end else ""
    4417
    4418        return f"CHANGES ({information}){at_before}{end}"

def pad_sql(self, expression: sqlglot.expressions.Pad) -> str: View Source

    4420    def pad_sql(self, expression: exp.Pad) -> str:
    4421        prefix = "L" if expression.args.get("is_left") else "R"
    4422
    4423        fill_pattern = self.sql(expression, "fill_pattern") or None
    4424        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4425            fill_pattern = "' '"
    4426
    4427        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)

def summarize_sql(self, expression: sqlglot.expressions.Summarize) -> str: View Source

    4429    def summarize_sql(self, expression: exp.Summarize) -> str:
    4430        table = " TABLE" if expression.args.get("table") else ""
    4431        return f"SUMMARIZE{table} {self.sql(expression.this)}"

def explodinggenerateseries_sql(self, expression: sqlglot.expressions.ExplodingGenerateSeries) -> str: View Source

    4433    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4434        generate_series = exp.GenerateSeries(**expression.args)
    4435
    4436        parent = expression.parent
    4437        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4438            parent = parent.parent
    4439
    4440        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4441            return self.sql(exp.Unnest(expressions=[generate_series]))
    4442
    4443        if isinstance(parent, exp.Select):
    4444            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4445
    4446        return self.sql(generate_series)

def arrayconcat_sql( self, expression: sqlglot.expressions.ArrayConcat, name: str = 'ARRAY_CONCAT') -> str: View Source

    4448    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4449        exprs = expression.expressions
    4450        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4451            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4452        else:
    4453            rhs = self.expressions(expression)
    4454
    4455        return self.func(name, expression.this, rhs or None)

def converttimezone_sql(self, expression: sqlglot.expressions.ConvertTimezone) -> str: View Source

    4457    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4458        if self.SUPPORTS_CONVERT_TIMEZONE:
    4459            return self.function_fallback_sql(expression)
    4460
    4461        source_tz = expression.args.get("source_tz")
    4462        target_tz = expression.args.get("target_tz")
    4463        timestamp = expression.args.get("timestamp")
    4464
    4465        if source_tz and timestamp:
    4466            timestamp = exp.AtTimeZone(
    4467                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4468            )
    4469
    4470        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4471
    4472        return self.sql(expr)

def json_sql(self, expression: sqlglot.expressions.JSON) -> str: View Source

    4474    def json_sql(self, expression: exp.JSON) -> str:
    4475        this = self.sql(expression, "this")
    4476        this = f" {this}" if this else ""
    4477
    4478        _with = expression.args.get("with")
    4479
    4480        if _with is None:
    4481            with_sql = ""
    4482        elif not _with:
    4483            with_sql = " WITHOUT"
    4484        else:
    4485            with_sql = " WITH"
    4486
    4487        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4488
    4489        return f"JSON{this}{with_sql}{unique_sql}"

def jsonvalue_sql(self, expression: sqlglot.expressions.JSONValue) -> str: View Source

    4491    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4492        def _generate_on_options(arg: t.Any) -> str:
    4493            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4494
    4495        path = self.sql(expression, "path")
    4496        returning = self.sql(expression, "returning")
    4497        returning = f" RETURNING {returning}" if returning else ""
    4498
    4499        on_condition = self.sql(expression, "on_condition")
    4500        on_condition = f" {on_condition}" if on_condition else ""
    4501
    4502        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")

def conditionalinsert_sql(self, expression: sqlglot.expressions.ConditionalInsert) -> str: View Source

    4504    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4505        else_ = "ELSE " if expression.args.get("else_") else ""
    4506        condition = self.sql(expression, "expression")
    4507        condition = f"WHEN {condition} THEN " if condition else else_
    4508        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4509        return f"{condition}{insert}"

def multitableinserts_sql(self, expression: sqlglot.expressions.MultitableInserts) -> str: View Source

    4511    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4512        kind = self.sql(expression, "kind")
    4513        expressions = self.seg(self.expressions(expression, sep=" "))
    4514        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4515        return res

def oncondition_sql(self, expression: sqlglot.expressions.OnCondition) -> str: View Source

    4517    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4518        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4519        empty = expression.args.get("empty")
    4520        empty = (
    4521            f"DEFAULT {empty} ON EMPTY"
    4522            if isinstance(empty, exp.Expression)
    4523            else self.sql(expression, "empty")
    4524        )
    4525
    4526        error = expression.args.get("error")
    4527        error = (
    4528            f"DEFAULT {error} ON ERROR"
    4529            if isinstance(error, exp.Expression)
    4530            else self.sql(expression, "error")
    4531        )
    4532
    4533        if error and empty:
    4534            error = (
    4535                f"{empty} {error}"
    4536                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4537                else f"{error} {empty}"
    4538            )
    4539            empty = ""
    4540
    4541        null = self.sql(expression, "null")
    4542
    4543        return f"{empty}{error}{null}"

def jsonextractquote_sql(self, expression: sqlglot.expressions.JSONExtractQuote) -> str: View Source

    4545    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
    4546        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
    4547        return f"{self.sql(expression, 'option')} QUOTES{scalar}"

def jsonexists_sql(self, expression: sqlglot.expressions.JSONExists) -> str: View Source

    4549    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4550        this = self.sql(expression, "this")
    4551        path = self.sql(expression, "path")
    4552
    4553        passing = self.expressions(expression, "passing")
    4554        passing = f" PASSING {passing}" if passing else ""
    4555
    4556        on_condition = self.sql(expression, "on_condition")
    4557        on_condition = f" {on_condition}" if on_condition else ""
    4558
    4559        path = f"{path}{passing}{on_condition}"
    4560
    4561        return self.func("JSON_EXISTS", this, path)

def arrayagg_sql(self, expression: sqlglot.expressions.ArrayAgg) -> str: View Source

    4563    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4564        array_agg = self.function_fallback_sql(expression)
    4565
    4566        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4567        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4568        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4569            parent = expression.parent
    4570            if isinstance(parent, exp.Filter):
    4571                parent_cond = parent.expression.this
    4572                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4573            else:
    4574                this = expression.this
    4575                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4576                if this.find(exp.Column):
    4577                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4578                    this_sql = (
    4579                        self.expressions(this)
    4580                        if isinstance(this, exp.Distinct)
    4581                        else self.sql(expression, "this")
    4582                    )
    4583
    4584                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4585
    4586        return array_agg

def apply_sql(self, expression: sqlglot.expressions.Apply) -> str: View Source

    4588    def apply_sql(self, expression: exp.Apply) -> str:
    4589        this = self.sql(expression, "this")
    4590        expr = self.sql(expression, "expression")
    4591
    4592        return f"{this} APPLY({expr})"

def grant_sql(self, expression: sqlglot.expressions.Grant) -> str: View Source

    4594    def grant_sql(self, expression: exp.Grant) -> str:
    4595        privileges_sql = self.expressions(expression, key="privileges", flat=True)
    4596
    4597        kind = self.sql(expression, "kind")
    4598        kind = f" {kind}" if kind else ""
    4599
    4600        securable = self.sql(expression, "securable")
    4601        securable = f" {securable}" if securable else ""
    4602
    4603        principals = self.expressions(expression, key="principals", flat=True)
    4604
    4605        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
    4606
    4607        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"

def grantprivilege_sql(self, expression: sqlglot.expressions.GrantPrivilege): View Source

    4609    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4610        this = self.sql(expression, "this")
    4611        columns = self.expressions(expression, flat=True)
    4612        columns = f"({columns})" if columns else ""
    4613
    4614        return f"{this}{columns}"

def grantprincipal_sql(self, expression: sqlglot.expressions.GrantPrincipal): View Source

    4616    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4617        this = self.sql(expression, "this")
    4618
    4619        kind = self.sql(expression, "kind")
    4620        kind = f"{kind} " if kind else ""
    4621
    4622        return f"{kind}{this}"

def columns_sql(self, expression: sqlglot.expressions.Columns): View Source

    4624    def columns_sql(self, expression: exp.Columns):
    4625        func = self.function_fallback_sql(expression)
    4626        if expression.args.get("unpack"):
    4627            func = f"*{func}"
    4628
    4629        return func

def overlay_sql(self, expression: sqlglot.expressions.Overlay): View Source

    4631    def overlay_sql(self, expression: exp.Overlay):
    4632        this = self.sql(expression, "this")
    4633        expr = self.sql(expression, "expression")
    4634        from_sql = self.sql(expression, "from")
    4635        for_sql = self.sql(expression, "for")
    4636        for_sql = f" FOR {for_sql}" if for_sql else ""
    4637
    4638        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"

@unsupported_args('format')
def todouble_sql(self, expression: sqlglot.expressions.ToDouble) -> str: View Source

    4640    @unsupported_args("format")
    4641    def todouble_sql(self, expression: exp.ToDouble) -> str:
    4642        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))

def string_sql(self, expression: sqlglot.expressions.String) -> str: View Source

    4644    def string_sql(self, expression: exp.String) -> str:
    4645        this = expression.this
    4646        zone = expression.args.get("zone")
    4647
    4648        if zone:
    4649            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4650            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4651            # set for source_tz to transpile the time conversion before the STRING cast
    4652            this = exp.ConvertTimezone(
    4653                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4654            )
    4655
    4656        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))

def median_sql(self, expression: sqlglot.expressions.Median): View Source

    4658    def median_sql(self, expression: exp.Median):
    4659        if not self.SUPPORTS_MEDIAN:
    4660            return self.sql(
    4661                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4662            )
    4663
    4664        return self.function_fallback_sql(expression)

def overflowtruncatebehavior_sql(self, expression: sqlglot.expressions.OverflowTruncateBehavior) -> str: View Source

    4666    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4667        filler = self.sql(expression, "this")
    4668        filler = f" {filler}" if filler else ""
    4669        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4670        return f"TRUNCATE{filler} {with_count}"

def unixseconds_sql(self, expression: sqlglot.expressions.UnixSeconds) -> str: View Source

    4672    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4673        if self.SUPPORTS_UNIX_SECONDS:
    4674            return self.function_fallback_sql(expression)
    4675
    4676        start_ts = exp.cast(
    4677            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4678        )
    4679
    4680        return self.sql(
    4681            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    4682        )

def arraysize_sql(self, expression: sqlglot.expressions.ArraySize) -> str: View Source

    4684    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    4685        dim = expression.expression
    4686
    4687        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    4688        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    4689            if not (dim.is_int and dim.name == "1"):
    4690                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    4691            dim = None
    4692
    4693        # If dimension is required but not specified, default initialize it
    4694        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    4695            dim = exp.Literal.number(1)
    4696
    4697        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)

def attach_sql(self, expression: sqlglot.expressions.Attach) -> str: View Source

    4699    def attach_sql(self, expression: exp.Attach) -> str:
    4700        this = self.sql(expression, "this")
    4701        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    4702        expressions = self.expressions(expression)
    4703        expressions = f" ({expressions})" if expressions else ""
    4704
    4705        return f"ATTACH{exists_sql} {this}{expressions}"

def detach_sql(self, expression: sqlglot.expressions.Detach) -> str: View Source

    4707    def detach_sql(self, expression: exp.Detach) -> str:
    4708        this = self.sql(expression, "this")
    4709        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    4710
    4711        return f"DETACH{exists_sql} {this}"

def attachoption_sql(self, expression: sqlglot.expressions.AttachOption) -> str: View Source

    4713    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    4714        this = self.sql(expression, "this")
    4715        value = self.sql(expression, "expression")
    4716        value = f" {value}" if value else ""
    4717        return f"{this}{value}"

def featuresattime_sql(self, expression: sqlglot.expressions.FeaturesAtTime) -> str: View Source

    4719    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4720        this_sql = self.sql(expression, "this")
    4721        if isinstance(expression.this, exp.Table):
    4722            this_sql = f"TABLE {this_sql}"
    4723
    4724        return self.func(
    4725            "FEATURES_AT_TIME",
    4726            this_sql,
    4727            expression.args.get("time"),
    4728            expression.args.get("num_rows"),
    4729            expression.args.get("ignore_feature_nulls"),
    4730        )

def watermarkcolumnconstraint_sql(self, expression: sqlglot.expressions.WatermarkColumnConstraint) -> str: View Source

    4732    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    4733        return (
    4734            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    4735        )

def encodeproperty_sql(self, expression: sqlglot.expressions.EncodeProperty) -> str: View Source

    4737    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    4738        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    4739        encode = f"{encode} {self.sql(expression, 'this')}"
    4740
    4741        properties = expression.args.get("properties")
    4742        if properties:
    4743            encode = f"{encode} {self.properties(properties)}"
    4744
    4745        return encode

def includeproperty_sql(self, expression: sqlglot.expressions.IncludeProperty) -> str: View Source

    4747    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    4748        this = self.sql(expression, "this")
    4749        include = f"INCLUDE {this}"
    4750
    4751        column_def = self.sql(expression, "column_def")
    4752        if column_def:
    4753            include = f"{include} {column_def}"
    4754
    4755        alias = self.sql(expression, "alias")
    4756        if alias:
    4757            include = f"{include} AS {alias}"
    4758
    4759        return include

def xmlelement_sql(self, expression: sqlglot.expressions.XMLElement) -> str: View Source

    4761    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
    4762        name = f"NAME {self.sql(expression, 'this')}"
    4763        return self.func("XMLELEMENT", name, *expression.expressions)

def partitionbyrangeproperty_sql(self, expression: sqlglot.expressions.PartitionByRangeProperty) -> str: View Source

    4765    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    4766        partitions = self.expressions(expression, "partition_expressions")
    4767        create = self.expressions(expression, "create_expressions")
    4768        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"

def partitionbyrangepropertydynamic_sql( self, expression: sqlglot.expressions.PartitionByRangePropertyDynamic) -> str: View Source

    4770    def partitionbyrangepropertydynamic_sql(
    4771        self, expression: exp.PartitionByRangePropertyDynamic
    4772    ) -> str:
    4773        start = self.sql(expression, "start")
    4774        end = self.sql(expression, "end")
    4775
    4776        every = expression.args["every"]
    4777        if isinstance(every, exp.Interval) and every.this.is_string:
    4778            every.this.replace(exp.Literal.number(every.name))
    4779
    4780        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"

def unpivotcolumns_sql(self, expression: sqlglot.expressions.UnpivotColumns) -> str: View Source

    4782    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    4783        name = self.sql(expression, "this")
    4784        values = self.expressions(expression, flat=True)
    4785
    4786        return f"NAME {name} VALUE {values}"

def analyzesample_sql(self, expression: sqlglot.expressions.AnalyzeSample) -> str: View Source

    4788    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    4789        kind = self.sql(expression, "kind")
    4790        sample = self.sql(expression, "sample")
    4791        return f"SAMPLE {sample} {kind}"

def analyzestatistics_sql(self, expression: sqlglot.expressions.AnalyzeStatistics) -> str: View Source

    4793    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    4794        kind = self.sql(expression, "kind")
    4795        option = self.sql(expression, "option")
    4796        option = f" {option}" if option else ""
    4797        this = self.sql(expression, "this")
    4798        this = f" {this}" if this else ""
    4799        columns = self.expressions(expression)
    4800        columns = f" {columns}" if columns else ""
    4801        return f"{kind}{option} STATISTICS{this}{columns}"

def analyzehistogram_sql(self, expression: sqlglot.expressions.AnalyzeHistogram) -> str: View Source

    4803    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    4804        this = self.sql(expression, "this")
    4805        columns = self.expressions(expression)
    4806        inner_expression = self.sql(expression, "expression")
    4807        inner_expression = f" {inner_expression}" if inner_expression else ""
    4808        update_options = self.sql(expression, "update_options")
    4809        update_options = f" {update_options} UPDATE" if update_options else ""
    4810        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"

def analyzedelete_sql(self, expression: sqlglot.expressions.AnalyzeDelete) -> str: View Source

    4812    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    4813        kind = self.sql(expression, "kind")
    4814        kind = f" {kind}" if kind else ""
    4815        return f"DELETE{kind} STATISTICS"

def analyzelistchainedrows_sql(self, expression: sqlglot.expressions.AnalyzeListChainedRows) -> str: View Source

    4817    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
    4818        inner_expression = self.sql(expression, "expression")
    4819        return f"LIST CHAINED ROWS{inner_expression}"

def analyzevalidate_sql(self, expression: sqlglot.expressions.AnalyzeValidate) -> str: View Source

    4821    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    4822        kind = self.sql(expression, "kind")
    4823        this = self.sql(expression, "this")
    4824        this = f" {this}" if this else ""
    4825        inner_expression = self.sql(expression, "expression")
    4826        return f"VALIDATE {kind}{this}{inner_expression}"

def analyze_sql(self, expression: sqlglot.expressions.Analyze) -> str: View Source

    4828    def analyze_sql(self, expression: exp.Analyze) -> str:
    4829        options = self.expressions(expression, key="options", sep=" ")
    4830        options = f" {options}" if options else ""
    4831        kind = self.sql(expression, "kind")
    4832        kind = f" {kind}" if kind else ""
    4833        this = self.sql(expression, "this")
    4834        this = f" {this}" if this else ""
    4835        mode = self.sql(expression, "mode")
    4836        mode = f" {mode}" if mode else ""
    4837        properties = self.sql(expression, "properties")
    4838        properties = f" {properties}" if properties else ""
    4839        partition = self.sql(expression, "partition")
    4840        partition = f" {partition}" if partition else ""
    4841        inner_expression = self.sql(expression, "expression")
    4842        inner_expression = f" {inner_expression}" if inner_expression else ""
    4843        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"

def xmltable_sql(self, expression: sqlglot.expressions.XMLTable) -> str: View Source

    4845    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    4846        this = self.sql(expression, "this")
    4847        namespaces = self.expressions(expression, key="namespaces")
    4848        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    4849        passing = self.expressions(expression, key="passing")
    4850        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    4851        columns = self.expressions(expression, key="columns")
    4852        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    4853        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    4854        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"

def xmlnamespace_sql(self, expression: sqlglot.expressions.XMLNamespace) -> str: View Source

    4856    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
    4857        this = self.sql(expression, "this")
    4858        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"

def export_sql(self, expression: sqlglot.expressions.Export) -> str: View Source

    4860    def export_sql(self, expression: exp.Export) -> str:
    4861        this = self.sql(expression, "this")
    4862        connection = self.sql(expression, "connection")
    4863        connection = f"WITH CONNECTION {connection} " if connection else ""
    4864        options = self.sql(expression, "options")
    4865        return f"EXPORT DATA {connection}{options} AS {this}"

def declare_sql(self, expression: sqlglot.expressions.Declare) -> str: View Source

    4867    def declare_sql(self, expression: exp.Declare) -> str:
    4868        return f"DECLARE {self.expressions(expression, flat=True)}"

def declareitem_sql(self, expression: sqlglot.expressions.DeclareItem) -> str: View Source

    4870    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
    4871        variable = self.sql(expression, "this")
    4872        default = self.sql(expression, "default")
    4873        default = f" = {default}" if default else ""
    4874
    4875        kind = self.sql(expression, "kind")
    4876        if isinstance(expression.args.get("kind"), exp.Schema):
    4877            kind = f"TABLE {kind}"
    4878
    4879        return f"{variable} AS {kind}{default}"

def recursivewithsearch_sql(self, expression: sqlglot.expressions.RecursiveWithSearch) -> str: View Source

    4881    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
    4882        kind = self.sql(expression, "kind")
    4883        this = self.sql(expression, "this")
    4884        set = self.sql(expression, "expression")
    4885        using = self.sql(expression, "using")
    4886        using = f" USING {using}" if using else ""
    4887
    4888        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
    4889
    4890        return f"{kind_sql} {this} SET {set}{using}"

def parameterizedagg_sql(self, expression: sqlglot.expressions.ParameterizedAgg) -> str: View Source

    4892    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
    4893        params = self.expressions(expression, key="params", flat=True)
    4894        return self.func(expression.name, *expression.expressions) + f"({params})"

def anonymousaggfunc_sql(self, expression: sqlglot.expressions.AnonymousAggFunc) -> str: View Source

    4896    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
    4897        return self.func(expression.name, *expression.expressions)

def combinedaggfunc_sql(self, expression: sqlglot.expressions.CombinedAggFunc) -> str: View Source

    4899    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
    4900        return self.anonymousaggfunc_sql(expression)

def combinedparameterizedagg_sql(self, expression: sqlglot.expressions.CombinedParameterizedAgg) -> str: View Source

    4902    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
    4903        return self.parameterizedagg_sql(expression)

def show_sql(self, expression: sqlglot.expressions.Show) -> str: View Source

    4905    def show_sql(self, expression: exp.Show) -> str:
    4906        self.unsupported("Unsupported SHOW statement")
    4907        return ""

def get_put_sql( self, expression: sqlglot.expressions.Put | sqlglot.expressions.Get) -> str: View Source

    4909    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
    4910        # Snowflake GET/PUT statements:
    4911        #   PUT <file> <internalStage> <properties>
    4912        #   GET <internalStage> <file> <properties>
    4913        props = expression.args.get("properties")
    4914        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
    4915        this = self.sql(expression, "this")
    4916        target = self.sql(expression, "target")
    4917
    4918        if isinstance(expression, exp.Put):
    4919            return f"PUT {this} {target}{props_sql}"
    4920        else:
    4921            return f"GET {target} {this}{props_sql}"