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

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

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

    739    def __init__(
    740        self,
    741        pretty: t.Optional[bool] = None,
    742        identify: str | bool = False,
    743        normalize: bool = False,
    744        pad: int = 2,
    745        indent: int = 2,
    746        normalize_functions: t.Optional[str | bool] = None,
    747        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    748        max_unsupported: int = 3,
    749        leading_comma: bool = False,
    750        max_text_width: int = 80,
    751        comments: bool = True,
    752        dialect: DialectType = None,
    753    ):
    754        import sqlglot
    755        from sqlglot.dialects import Dialect
    756
    757        self.pretty = pretty if pretty is not None else sqlglot.pretty
    758        self.identify = identify
    759        self.normalize = normalize
    760        self.pad = pad
    761        self._indent = indent
    762        self.unsupported_level = unsupported_level
    763        self.max_unsupported = max_unsupported
    764        self.leading_comma = leading_comma
    765        self.max_text_width = max_text_width
    766        self.comments = comments
    767        self.dialect = Dialect.get_or_raise(dialect)
    768
    769        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    770        self.normalize_functions = (
    771            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    772        )
    773
    774        self.unsupported_messages: t.List[str] = []
    775        self._escaped_quote_end: str = (
    776            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    777        )
    778        self._escaped_byte_quote_end: str = (
    779            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.BYTE_END
    780            if self.dialect.BYTE_END
    781            else ""
    782        )
    783        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
    784
    785        self._next_name = name_sequence("_t")
    786
    787        self._identifier_start = self.dialect.IDENTIFIER_START
    788        self._identifier_end = self.dialect.IDENTIFIER_END
    789
    790        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.ConvertToCharset'>: <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.EnviromentProperty'>: <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.JSONBContainsAnyTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONBContainsAllTopKeys'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONBDeleteAtPath'>: <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.PositionalColumn'>: <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.TableColumn'>: <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.UtcDate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UtcTime'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UtcTimestamp'>: <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.WeekStart'>: <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.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SUPPORTS_WINDOW_EXCLUDE = False
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
UNICODE_SUBSTITUTE: Optional[Callable[[re.Match[str]], str]] = None
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
ALTER_SET_WRAPPED = False
NORMALIZE_EXTRACT_DATE_PARTS = 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
SUPPORTS_DECODE_CASE = True
SUPPORTS_BETWEEN_FLAGS = False
SUPPORTS_LIKE_QUANTIFIERS = True
MATCH_AGAINST_TABLE_PREFIX: Optional[str] = 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'}
UNSUPPORTED_TYPES: set[sqlglot.expressions.DataType.Type] = set()
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.EnviromentProperty'>: <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.Order'>, <class 'sqlglot.expressions.Group'>, <class 'sqlglot.expressions.Having'>, <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.NCHAR: 'NCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>, <Type.VARCHAR: 'VARCHAR'>, <Type.CHAR: 'CHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
RESPECT_IGNORE_NULLS_UNSUPPORTED_EXPRESSIONS: Tuple[Type[sqlglot.expressions.Expression], ...] = ()
SAFE_JSON_PATH_KEY_RE = re.compile('^[_a-zA-Z][\\w]*$')
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

    792    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
    793        """
    794        Generates the SQL string corresponding to the given syntax tree.
    795
    796        Args:
    797            expression: The syntax tree.
    798            copy: Whether to copy the expression. The generator performs mutations so
    799                it is safer to copy.
    800
    801        Returns:
    802            The SQL string corresponding to `expression`.
    803        """
    804        if copy:
    805            expression = expression.copy()
    806
    807        expression = self.preprocess(expression)
    808
    809        self.unsupported_messages = []
    810        sql = self.sql(expression).strip()
    811
    812        if self.pretty:
    813            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    814
    815        if self.unsupported_level == ErrorLevel.IGNORE:
    816            return sql
    817
    818        if self.unsupported_level == ErrorLevel.WARN:
    819            for msg in self.unsupported_messages:
    820                logger.warning(msg)
    821        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    822            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    823
    824        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

    826    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    827        """Apply generic preprocessing transformations to a given expression."""
    828        expression = self._move_ctes_to_top_level(expression)
    829
    830        if self.ENSURE_BOOLS:
    831            from sqlglot.transforms import ensure_bools
    832
    833            expression = ensure_bools(expression)
    834
    835        return expression

Apply generic preprocessing transformations to a given expression.

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

    848    def unsupported(self, message: str) -> None:
    849        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    850            raise UnsupportedError(message)
    851        self.unsupported_messages.append(message)

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

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

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

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

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

    859    def sanitize_comment(self, comment: str) -> str:
    860        comment = " " + comment if comment[0].strip() else comment
    861        comment = comment + " " if comment[-1].strip() else comment
    862
    863        if not self.dialect.tokenizer_class.NESTED_COMMENTS:
    864            # Necessary workaround to avoid syntax errors due to nesting: /* ... */ ... */
    865            comment = comment.replace("*/", "* /")
    866
    867        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

    869    def maybe_comment(
    870        self,
    871        sql: str,
    872        expression: t.Optional[exp.Expression] = None,
    873        comments: t.Optional[t.List[str]] = None,
    874        separated: bool = False,
    875    ) -> str:
    876        comments = (
    877            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    878            if self.comments
    879            else None
    880        )
    881
    882        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    883            return sql
    884
    885        comments_sql = " ".join(
    886            f"/*{self.sanitize_comment(comment)}*/" for comment in comments if comment
    887        )
    888
    889        if not comments_sql:
    890            return sql
    891
    892        comments_sql = self._replace_line_breaks(comments_sql)
    893
    894        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    895            return (
    896                f"{self.sep()}{comments_sql}{sql}"
    897                if not sql or sql[0].isspace()
    898                else f"{comments_sql}{self.sep()}{sql}"
    899            )
    900
    901        return f"{sql} {comments_sql}"

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

    903    def wrap(self, expression: exp.Expression | str) -> str:
    904        this_sql = (
    905            self.sql(expression)
    906            if isinstance(expression, exp.UNWRAPPED_QUERIES)
    907            else self.sql(expression, "this")
    908        )
    909        if not this_sql:
    910            return "()"
    911
    912        this_sql = self.indent(this_sql, level=1, pad=0)
    913        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    915    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    916        original = self.identify
    917        self.identify = False
    918        result = func(*args, **kwargs)
    919        self.identify = original
    920        return result

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

    922    def normalize_func(self, name: str) -> str:
    923        if self.normalize_functions == "upper" or self.normalize_functions is True:
    924            return name.upper()
    925        if self.normalize_functions == "lower":
    926            return name.lower()
    927        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

    929    def indent(
    930        self,
    931        sql: str,
    932        level: int = 0,
    933        pad: t.Optional[int] = None,
    934        skip_first: bool = False,
    935        skip_last: bool = False,
    936    ) -> str:
    937        if not self.pretty or not sql:
    938            return sql
    939
    940        pad = self.pad if pad is None else pad
    941        lines = sql.split("\n")
    942
    943        return "\n".join(
    944            (
    945                line
    946                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    947                else f"{' ' * (level * self._indent + pad)}{line}"
    948            )
    949            for i, line in enumerate(lines)
    950        )

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

    952    def sql(
    953        self,
    954        expression: t.Optional[str | exp.Expression],
    955        key: t.Optional[str] = None,
    956        comment: bool = True,
    957    ) -> str:
    958        if not expression:
    959            return ""
    960
    961        if isinstance(expression, str):
    962            return expression
    963
    964        if key:
    965            value = expression.args.get(key)
    966            if value:
    967                return self.sql(value)
    968            return ""
    969
    970        transform = self.TRANSFORMS.get(expression.__class__)
    971
    972        if callable(transform):
    973            sql = transform(self, expression)
    974        elif isinstance(expression, exp.Expression):
    975            exp_handler_name = f"{expression.key}_sql"
    976
    977            if hasattr(self, exp_handler_name):
    978                sql = getattr(self, exp_handler_name)(expression)
    979            elif isinstance(expression, exp.Func):
    980                sql = self.function_fallback_sql(expression)
    981            elif isinstance(expression, exp.Property):
    982                sql = self.property_sql(expression)
    983            else:
    984                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    985        else:
    986            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    987
    988        return self.maybe_comment(sql, expression) if self.comments and comment else sql

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

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

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

     995    def cache_sql(self, expression: exp.Cache) -> str:
     996        lazy = " LAZY" if expression.args.get("lazy") else ""
     997        table = self.sql(expression, "this")
     998        options = expression.args.get("options")
     999        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    1000        sql = self.sql(expression, "expression")
    1001        sql = f" AS{self.sep()}{sql}" if sql else ""
    1002        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    1003        return self.prepend_ctes(expression, sql)

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

    1005    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    1006        if isinstance(expression.parent, exp.Cast):
    1007            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    1008        default = "DEFAULT " if expression.args.get("default") else ""
    1009        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    1011    def column_parts(self, expression: exp.Column) -> str:
    1012        return ".".join(
    1013            self.sql(part)
    1014            for part in (
    1015                expression.args.get("catalog"),
    1016                expression.args.get("db"),
    1017                expression.args.get("table"),
    1018                expression.args.get("this"),
    1019            )
    1020            if part
    1021        )

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

    1023    def column_sql(self, expression: exp.Column) -> str:
    1024        join_mark = " (+)" if expression.args.get("join_mark") else ""
    1025
    1026        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
    1027            join_mark = ""
    1028            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    1029
    1030        return f"{self.column_parts(expression)}{join_mark}"

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

    1032    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    1033        this = self.sql(expression, "this")
    1034        this = f" {this}" if this else ""
    1035        position = self.sql(expression, "position")
    1036        return f"{position}{this}"

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

    1038    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    1039        column = self.sql(expression, "this")
    1040        kind = self.sql(expression, "kind")
    1041        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    1042        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    1043        kind = f"{sep}{kind}" if kind else ""
    1044        constraints = f" {constraints}" if constraints else ""
    1045        position = self.sql(expression, "position")
    1046        position = f" {position}" if position else ""
    1047
    1048        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    1049            kind = ""
    1050
    1051        return f"{exists}{column}{kind}{constraints}{position}"

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

    1053    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    1054        this = self.sql(expression, "this")
    1055        kind_sql = self.sql(expression, "kind").strip()
    1056        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    1058    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    1059        this = self.sql(expression, "this")
    1060        if expression.args.get("not_null"):
    1061            persisted = " PERSISTED NOT NULL"
    1062        elif expression.args.get("persisted"):
    1063            persisted = " PERSISTED"
    1064        else:
    1065            persisted = ""
    1066
    1067        return f"AS {this}{persisted}"

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

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

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

    1072    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    1073        if isinstance(expression.this, list):
    1074            this = self.wrap(self.expressions(expression, key="this", flat=True))
    1075        else:
    1076            this = self.sql(expression, "this")
    1077
    1078        return f"COMPRESS {this}"

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

    1080    def generatedasidentitycolumnconstraint_sql(
    1081        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1082    ) -> str:
    1083        this = ""
    1084        if expression.this is not None:
    1085            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1086            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1087
    1088        start = expression.args.get("start")
    1089        start = f"START WITH {start}" if start else ""
    1090        increment = expression.args.get("increment")
    1091        increment = f" INCREMENT BY {increment}" if increment else ""
    1092        minvalue = expression.args.get("minvalue")
    1093        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1094        maxvalue = expression.args.get("maxvalue")
    1095        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1096        cycle = expression.args.get("cycle")
    1097        cycle_sql = ""
    1098
    1099        if cycle is not None:
    1100            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1101            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1102
    1103        sequence_opts = ""
    1104        if start or increment or cycle_sql:
    1105            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1106            sequence_opts = f" ({sequence_opts.strip()})"
    1107
    1108        expr = self.sql(expression, "expression")
    1109        expr = f"({expr})" if expr else "IDENTITY"
    1110
    1111        return f"GENERATED{this} AS {expr}{sequence_opts}"

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

    1113    def generatedasrowcolumnconstraint_sql(
    1114        self, expression: exp.GeneratedAsRowColumnConstraint
    1115    ) -> str:
    1116        start = "START" if expression.args.get("start") else "END"
    1117        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1118        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    1120    def periodforsystemtimeconstraint_sql(
    1121        self, expression: exp.PeriodForSystemTimeConstraint
    1122    ) -> str:
    1123        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

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

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

    1128    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1129        desc = expression.args.get("desc")
    1130        if desc is not None:
    1131            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1132        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1133        options = f" {options}" if options else ""
    1134        return f"PRIMARY KEY{options}"

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

    1136    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1137        this = self.sql(expression, "this")
    1138        this = f" {this}" if this else ""
    1139        index_type = expression.args.get("index_type")
    1140        index_type = f" USING {index_type}" if index_type else ""
    1141        on_conflict = self.sql(expression, "on_conflict")
    1142        on_conflict = f" {on_conflict}" if on_conflict else ""
    1143        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1144        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1145        options = f" {options}" if options else ""
    1146        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"

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

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

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

    1151    def create_sql(self, expression: exp.Create) -> str:
    1152        kind = self.sql(expression, "kind")
    1153        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1154        properties = expression.args.get("properties")
    1155        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1156
    1157        this = self.createable_sql(expression, properties_locs)
    1158
    1159        properties_sql = ""
    1160        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1161            exp.Properties.Location.POST_WITH
    1162        ):
    1163            props_ast = exp.Properties(
    1164                expressions=[
    1165                    *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1166                    *properties_locs[exp.Properties.Location.POST_WITH],
    1167                ]
    1168            )
    1169            props_ast.parent = expression
    1170            properties_sql = self.sql(props_ast)
    1171
    1172            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1173                properties_sql = self.sep() + properties_sql
    1174            elif not self.pretty:
    1175                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1176                properties_sql = f" {properties_sql}"
    1177
    1178        begin = " BEGIN" if expression.args.get("begin") else ""
    1179        end = " END" if expression.args.get("end") else ""
    1180
    1181        expression_sql = self.sql(expression, "expression")
    1182        if expression_sql:
    1183            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1184
    1185            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1186                postalias_props_sql = ""
    1187                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1188                    postalias_props_sql = self.properties(
    1189                        exp.Properties(
    1190                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1191                        ),
    1192                        wrapped=False,
    1193                    )
    1194                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1195                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1196
    1197        postindex_props_sql = ""
    1198        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1199            postindex_props_sql = self.properties(
    1200                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1201                wrapped=False,
    1202                prefix=" ",
    1203            )
    1204
    1205        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1206        indexes = f" {indexes}" if indexes else ""
    1207        index_sql = indexes + postindex_props_sql
    1208
    1209        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1210        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1211        unique = " UNIQUE" if expression.args.get("unique") else ""
    1212
    1213        clustered = expression.args.get("clustered")
    1214        if clustered is None:
    1215            clustered_sql = ""
    1216        elif clustered:
    1217            clustered_sql = " CLUSTERED COLUMNSTORE"
    1218        else:
    1219            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1220
    1221        postcreate_props_sql = ""
    1222        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1223            postcreate_props_sql = self.properties(
    1224                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1225                sep=" ",
    1226                prefix=" ",
    1227                wrapped=False,
    1228            )
    1229
    1230        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1231
    1232        postexpression_props_sql = ""
    1233        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1234            postexpression_props_sql = self.properties(
    1235                exp.Properties(
    1236                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1237                ),
    1238                sep=" ",
    1239                prefix=" ",
    1240                wrapped=False,
    1241            )
    1242
    1243        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1244        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1245        no_schema_binding = (
    1246            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1247        )
    1248
    1249        clone = self.sql(expression, "clone")
    1250        clone = f" {clone}" if clone else ""
    1251
    1252        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1253            properties_expression = f"{expression_sql}{properties_sql}"
    1254        else:
    1255            properties_expression = f"{properties_sql}{expression_sql}"
    1256
    1257        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1258        return self.prepend_ctes(expression, expression_sql)

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

    1260    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1261        start = self.sql(expression, "start")
    1262        start = f"START WITH {start}" if start else ""
    1263        increment = self.sql(expression, "increment")
    1264        increment = f" INCREMENT BY {increment}" if increment else ""
    1265        minvalue = self.sql(expression, "minvalue")
    1266        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1267        maxvalue = self.sql(expression, "maxvalue")
    1268        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1269        owned = self.sql(expression, "owned")
    1270        owned = f" OWNED BY {owned}" if owned else ""
    1271
    1272        cache = expression.args.get("cache")
    1273        if cache is None:
    1274            cache_str = ""
    1275        elif cache is True:
    1276            cache_str = " CACHE"
    1277        else:
    1278            cache_str = f" CACHE {cache}"
    1279
    1280        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1281        options = f" {options}" if options else ""
    1282
    1283        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()

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

    1285    def clone_sql(self, expression: exp.Clone) -> str:
    1286        this = self.sql(expression, "this")
    1287        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1288        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1289        return f"{shallow}{keyword} {this}"

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

    1291    def describe_sql(self, expression: exp.Describe) -> str:
    1292        style = expression.args.get("style")
    1293        style = f" {style}" if style else ""
    1294        partition = self.sql(expression, "partition")
    1295        partition = f" {partition}" if partition else ""
    1296        format = self.sql(expression, "format")
    1297        format = f" {format}" if format else ""
    1298
    1299        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"

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

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

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

    1305    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1306        with_ = self.sql(expression, "with")
    1307        if with_:
    1308            sql = f"{with_}{self.sep()}{sql}"
    1309        return sql

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

    1311    def with_sql(self, expression: exp.With) -> str:
    1312        sql = self.expressions(expression, flat=True)
    1313        recursive = (
    1314            "RECURSIVE "
    1315            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1316            else ""
    1317        )
    1318        search = self.sql(expression, "search")
    1319        search = f" {search}" if search else ""
    1320
    1321        return f"WITH {recursive}{sql}{search}"

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

    1323    def cte_sql(self, expression: exp.CTE) -> str:
    1324        alias = expression.args.get("alias")
    1325        if alias:
    1326            alias.add_comments(expression.pop_comments())
    1327
    1328        alias_sql = self.sql(expression, "alias")
    1329
    1330        materialized = expression.args.get("materialized")
    1331        if materialized is False:
    1332            materialized = "NOT MATERIALIZED "
    1333        elif materialized:
    1334            materialized = "MATERIALIZED "
    1335
    1336        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"

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

    1338    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1339        alias = self.sql(expression, "this")
    1340        columns = self.expressions(expression, key="columns", flat=True)
    1341        columns = f"({columns})" if columns else ""
    1342
    1343        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1344            columns = ""
    1345            self.unsupported("Named columns are not supported in table alias.")
    1346
    1347        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1348            alias = self._next_name()
    1349
    1350        return f"{alias}{columns}"

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

    1352    def bitstring_sql(self, expression: exp.BitString) -> str:
    1353        this = self.sql(expression, "this")
    1354        if self.dialect.BIT_START:
    1355            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1356        return f"{int(this, 2)}"

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

    1358    def hexstring_sql(
    1359        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
    1360    ) -> str:
    1361        this = self.sql(expression, "this")
    1362        is_integer_type = expression.args.get("is_integer")
    1363
    1364        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
    1365            not self.dialect.HEX_START and not binary_function_repr
    1366        ):
    1367            # Integer representation will be returned if:
    1368            # - The read dialect treats the hex value as integer literal but not the write
    1369            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
    1370            return f"{int(this, 16)}"
    1371
    1372        if not is_integer_type:
    1373            # Read dialect treats the hex value as BINARY/BLOB
    1374            if binary_function_repr:
    1375                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
    1376                return self.func(binary_function_repr, exp.Literal.string(this))
    1377            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
    1378                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
    1379                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
    1380
    1381        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"

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

    1383    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1384        this = self.sql(expression, "this")
    1385        if self.dialect.BYTE_START:
    1386            escaped_byte_string = self.escape_str(
    1387                this,
    1388                escape_backslash=False,
    1389                delimiter=self.dialect.BYTE_END,
    1390                escaped_delimiter=self._escaped_byte_quote_end,
    1391            )
    1392            return f"{self.dialect.BYTE_START}{escaped_byte_string}{self.dialect.BYTE_END}"
    1393        return this

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

    1395    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1396        this = self.sql(expression, "this")
    1397        escape = expression.args.get("escape")
    1398
    1399        if self.dialect.UNICODE_START:
    1400            escape_substitute = r"\\\1"
    1401            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1402        else:
    1403            escape_substitute = r"\\u\1"
    1404            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1405
    1406        if escape:
    1407            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1408            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1409        else:
    1410            escape_pattern = ESCAPED_UNICODE_RE
    1411            escape_sql = ""
    1412
    1413        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1414            this = escape_pattern.sub(self.UNICODE_SUBSTITUTE or escape_substitute, this)
    1415
    1416        return f"{left_quote}{this}{right_quote}{escape_sql}"

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

    1418    def rawstring_sql(self, expression: exp.RawString) -> str:
    1419        string = expression.this
    1420        if "\\" in self.dialect.tokenizer_class.STRING_ESCAPES:
    1421            string = string.replace("\\", "\\\\")
    1422
    1423        string = self.escape_str(string, escape_backslash=False)
    1424        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"

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

    1426    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1427        this = self.sql(expression, "this")
    1428        specifier = self.sql(expression, "expression")
    1429        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1430        return f"{this}{specifier}"

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

    1432    def datatype_sql(self, expression: exp.DataType) -> str:
    1433        nested = ""
    1434        values = ""
    1435        interior = self.expressions(expression, flat=True)
    1436
    1437        type_value = expression.this
    1438        if type_value in self.UNSUPPORTED_TYPES:
    1439            self.unsupported(
    1440                f"Data type {type_value.value} is not supported when targeting {self.dialect.__class__.__name__}"
    1441            )
    1442
    1443        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1444            type_sql = self.sql(expression, "kind")
    1445        else:
    1446            type_sql = (
    1447                self.TYPE_MAPPING.get(type_value, type_value.value)
    1448                if isinstance(type_value, exp.DataType.Type)
    1449                else type_value
    1450            )
    1451
    1452        if interior:
    1453            if expression.args.get("nested"):
    1454                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1455                if expression.args.get("values") is not None:
    1456                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1457                    values = self.expressions(expression, key="values", flat=True)
    1458                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1459            elif type_value == exp.DataType.Type.INTERVAL:
    1460                nested = f" {interior}"
    1461            else:
    1462                nested = f"({interior})"
    1463
    1464        type_sql = f"{type_sql}{nested}{values}"
    1465        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1466            exp.DataType.Type.TIMETZ,
    1467            exp.DataType.Type.TIMESTAMPTZ,
    1468        ):
    1469            type_sql = f"{type_sql} WITH TIME ZONE"
    1470
    1471        return type_sql

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

    1473    def directory_sql(self, expression: exp.Directory) -> str:
    1474        local = "LOCAL " if expression.args.get("local") else ""
    1475        row_format = self.sql(expression, "row_format")
    1476        row_format = f" {row_format}" if row_format else ""
    1477        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1479    def delete_sql(self, expression: exp.Delete) -> str:
    1480        this = self.sql(expression, "this")
    1481        this = f" FROM {this}" if this else ""
    1482        using = self.sql(expression, "using")
    1483        using = f" USING {using}" if using else ""
    1484        cluster = self.sql(expression, "cluster")
    1485        cluster = f" {cluster}" if cluster else ""
    1486        where = self.sql(expression, "where")
    1487        returning = self.sql(expression, "returning")
    1488        limit = self.sql(expression, "limit")
    1489        tables = self.expressions(expression, key="tables")
    1490        tables = f" {tables}" if tables else ""
    1491        if self.RETURNING_END:
    1492            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1493        else:
    1494            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1495        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1497    def drop_sql(self, expression: exp.Drop) -> str:
    1498        this = self.sql(expression, "this")
    1499        expressions = self.expressions(expression, flat=True)
    1500        expressions = f" ({expressions})" if expressions else ""
    1501        kind = expression.args["kind"]
    1502        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1503        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1504        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1505        on_cluster = self.sql(expression, "cluster")
    1506        on_cluster = f" {on_cluster}" if on_cluster else ""
    1507        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1508        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1509        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1510        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1511        purge = " PURGE" if expression.args.get("purge") else ""
    1512        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

    1514    def set_operation(self, expression: exp.SetOperation) -> str:
    1515        op_type = type(expression)
    1516        op_name = op_type.key.upper()
    1517
    1518        distinct = expression.args.get("distinct")
    1519        if (
    1520            distinct is False
    1521            and op_type in (exp.Except, exp.Intersect)
    1522            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1523        ):
    1524            self.unsupported(f"{op_name} ALL is not supported")
    1525
    1526        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1527
    1528        if distinct is None:
    1529            distinct = default_distinct
    1530            if distinct is None:
    1531                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1532
    1533        if distinct is default_distinct:
    1534            distinct_or_all = ""
    1535        else:
    1536            distinct_or_all = " DISTINCT" if distinct else " ALL"
    1537
    1538        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
    1539        side_kind = f"{side_kind} " if side_kind else ""
    1540
    1541        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1542        on = self.expressions(expression, key="on", flat=True)
    1543        on = f" ON ({on})" if on else ""
    1544
    1545        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"

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

    1547    def set_operations(self, expression: exp.SetOperation) -> str:
    1548        if not self.SET_OP_MODIFIERS:
    1549            limit = expression.args.get("limit")
    1550            order = expression.args.get("order")
    1551
    1552            if limit or order:
    1553                select = self._move_ctes_to_top_level(
    1554                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1555                )
    1556
    1557                if limit:
    1558                    select = select.limit(limit.pop(), copy=False)
    1559                if order:
    1560                    select = select.order_by(order.pop(), copy=False)
    1561                return self.sql(select)
    1562
    1563        sqls: t.List[str] = []
    1564        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1565
    1566        while stack:
    1567            node = stack.pop()
    1568
    1569            if isinstance(node, exp.SetOperation):
    1570                stack.append(node.expression)
    1571                stack.append(
    1572                    self.maybe_comment(
    1573                        self.set_operation(node), comments=node.comments, separated=True
    1574                    )
    1575                )
    1576                stack.append(node.this)
    1577            else:
    1578                sqls.append(self.sql(node))
    1579
    1580        this = self.sep().join(sqls)
    1581        this = self.query_modifiers(expression, this)
    1582        return self.prepend_ctes(expression, this)

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

    1584    def fetch_sql(self, expression: exp.Fetch) -> str:
    1585        direction = expression.args.get("direction")
    1586        direction = f" {direction}" if direction else ""
    1587        count = self.sql(expression, "count")
    1588        count = f" {count}" if count else ""
    1589        limit_options = self.sql(expression, "limit_options")
    1590        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
    1591        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"

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

    1593    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
    1594        percent = " PERCENT" if expression.args.get("percent") else ""
    1595        rows = " ROWS" if expression.args.get("rows") else ""
    1596        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
    1597        if not with_ties and rows:
    1598            with_ties = " ONLY"
    1599        return f"{percent}{rows}{with_ties}"

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

    1601    def filter_sql(self, expression: exp.Filter) -> str:
    1602        if self.AGGREGATE_FILTER_SUPPORTED:
    1603            this = self.sql(expression, "this")
    1604            where = self.sql(expression, "expression").strip()
    1605            return f"{this} FILTER({where})"
    1606
    1607        agg = expression.this
    1608        agg_arg = agg.this
    1609        cond = expression.expression.this
    1610        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1611        return self.sql(agg)

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

    1613    def hint_sql(self, expression: exp.Hint) -> str:
    1614        if not self.QUERY_HINTS:
    1615            self.unsupported("Hints are not supported")
    1616            return ""
    1617
    1618        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1620    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1621        using = self.sql(expression, "using")
    1622        using = f" USING {using}" if using else ""
    1623        columns = self.expressions(expression, key="columns", flat=True)
    1624        columns = f"({columns})" if columns else ""
    1625        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1626        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1627        where = self.sql(expression, "where")
    1628        include = self.expressions(expression, key="include", flat=True)
    1629        if include:
    1630            include = f" INCLUDE ({include})"
    1631        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1632        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1633        tablespace = self.sql(expression, "tablespace")
    1634        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1635        on = self.sql(expression, "on")
    1636        on = f" ON {on}" if on else ""
    1637
    1638        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"

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

    1640    def index_sql(self, expression: exp.Index) -> str:
    1641        unique = "UNIQUE " if expression.args.get("unique") else ""
    1642        primary = "PRIMARY " if expression.args.get("primary") else ""
    1643        amp = "AMP " if expression.args.get("amp") else ""
    1644        name = self.sql(expression, "this")
    1645        name = f"{name} " if name else ""
    1646        table = self.sql(expression, "table")
    1647        table = f"{self.INDEX_ON} {table}" if table else ""
    1648
    1649        index = "INDEX " if not table else ""
    1650
    1651        params = self.sql(expression, "params")
    1652        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

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

    1654    def identifier_sql(self, expression: exp.Identifier) -> str:
    1655        text = expression.name
    1656        lower = text.lower()
    1657        text = lower if self.normalize and not expression.quoted else text
    1658        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1659        if (
    1660            expression.quoted
    1661            or self.dialect.can_identify(text, self.identify)
    1662            or lower in self.RESERVED_KEYWORDS
    1663            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1664        ):
    1665            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1666        return text

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

    1668    def hex_sql(self, expression: exp.Hex) -> str:
    1669        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1670        if self.dialect.HEX_LOWERCASE:
    1671            text = self.func("LOWER", text)
    1672
    1673        return text

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

    1675    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1676        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1677        if not self.dialect.HEX_LOWERCASE:
    1678            text = self.func("LOWER", text)
    1679        return text

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

    1681    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1682        input_format = self.sql(expression, "input_format")
    1683        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1684        output_format = self.sql(expression, "output_format")
    1685        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1686        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1696    def properties_sql(self, expression: exp.Properties) -> str:
    1697        root_properties = []
    1698        with_properties = []
    1699
    1700        for p in expression.expressions:
    1701            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1702            if p_loc == exp.Properties.Location.POST_WITH:
    1703                with_properties.append(p)
    1704            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1705                root_properties.append(p)
    1706
    1707        root_props_ast = exp.Properties(expressions=root_properties)
    1708        root_props_ast.parent = expression.parent
    1709
    1710        with_props_ast = exp.Properties(expressions=with_properties)
    1711        with_props_ast.parent = expression.parent
    1712
    1713        root_props = self.root_properties(root_props_ast)
    1714        with_props = self.with_properties(with_props_ast)
    1715
    1716        if root_props and with_props and not self.pretty:
    1717            with_props = " " + with_props
    1718
    1719        return root_props + with_props

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

    1721    def root_properties(self, properties: exp.Properties) -> str:
    1722        if properties.expressions:
    1723            return self.expressions(properties, indent=False, sep=" ")
    1724        return ""

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

    1726    def properties(
    1727        self,
    1728        properties: exp.Properties,
    1729        prefix: str = "",
    1730        sep: str = ", ",
    1731        suffix: str = "",
    1732        wrapped: bool = True,
    1733    ) -> str:
    1734        if properties.expressions:
    1735            expressions = self.expressions(properties, sep=sep, indent=False)
    1736            if expressions:
    1737                expressions = self.wrap(expressions) if wrapped else expressions
    1738                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1739        return ""

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

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

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

    1744    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1745        properties_locs = defaultdict(list)
    1746        for p in properties.expressions:
    1747            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1748            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1749                properties_locs[p_loc].append(p)
    1750            else:
    1751                self.unsupported(f"Unsupported property {p.key}")
    1752
    1753        return properties_locs

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

    1755    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1756        if isinstance(expression.this, exp.Dot):
    1757            return self.sql(expression, "this")
    1758        return f"'{expression.name}'" if string_key else expression.name

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

    1760    def property_sql(self, expression: exp.Property) -> str:
    1761        property_cls = expression.__class__
    1762        if property_cls == exp.Property:
    1763            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1764
    1765        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1766        if not property_name:
    1767            self.unsupported(f"Unsupported property {expression.key}")
    1768
    1769        return f"{property_name}={self.sql(expression, 'this')}"

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

    1771    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1772        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1773            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1774            options = f" {options}" if options else ""
    1775
    1776            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1777            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1778                like = f"({like})"
    1779
    1780            return like
    1781
    1782        if expression.expressions:
    1783            self.unsupported("Transpilation of LIKE property options is unsupported")
    1784
    1785        select = exp.select("*").from_(expression.this).limit(0)
    1786        return f"AS {self.sql(select)}"

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

    1788    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1789        no = "NO " if expression.args.get("no") else ""
    1790        protection = " PROTECTION" if expression.args.get("protection") else ""
    1791        return f"{no}FALLBACK{protection}"

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

    1793    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1794        no = "NO " if expression.args.get("no") else ""
    1795        local = expression.args.get("local")
    1796        local = f"{local} " if local else ""
    1797        dual = "DUAL " if expression.args.get("dual") else ""
    1798        before = "BEFORE " if expression.args.get("before") else ""
    1799        after = "AFTER " if expression.args.get("after") else ""
    1800        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1802    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1803        freespace = self.sql(expression, "this")
    1804        percent = " PERCENT" if expression.args.get("percent") else ""
    1805        return f"FREESPACE={freespace}{percent}"

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

    1807    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1808        if expression.args.get("default"):
    1809            property = "DEFAULT"
    1810        elif expression.args.get("on"):
    1811            property = "ON"
    1812        else:
    1813            property = "OFF"
    1814        return f"CHECKSUM={property}"

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

    1816    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1817        if expression.args.get("no"):
    1818            return "NO MERGEBLOCKRATIO"
    1819        if expression.args.get("default"):
    1820            return "DEFAULT MERGEBLOCKRATIO"
    1821
    1822        percent = " PERCENT" if expression.args.get("percent") else ""
    1823        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1825    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1826        default = expression.args.get("default")
    1827        minimum = expression.args.get("minimum")
    1828        maximum = expression.args.get("maximum")
    1829        if default or minimum or maximum:
    1830            if default:
    1831                prop = "DEFAULT"
    1832            elif minimum:
    1833                prop = "MINIMUM"
    1834            else:
    1835                prop = "MAXIMUM"
    1836            return f"{prop} DATABLOCKSIZE"
    1837        units = expression.args.get("units")
    1838        units = f" {units}" if units else ""
    1839        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1841    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1842        autotemp = expression.args.get("autotemp")
    1843        always = expression.args.get("always")
    1844        default = expression.args.get("default")
    1845        manual = expression.args.get("manual")
    1846        never = expression.args.get("never")
    1847
    1848        if autotemp is not None:
    1849            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1850        elif always:
    1851            prop = "ALWAYS"
    1852        elif default:
    1853            prop = "DEFAULT"
    1854        elif manual:
    1855            prop = "MANUAL"
    1856        elif never:
    1857            prop = "NEVER"
    1858        return f"BLOCKCOMPRESSION={prop}"

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

    1860    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1861        no = expression.args.get("no")
    1862        no = " NO" if no else ""
    1863        concurrent = expression.args.get("concurrent")
    1864        concurrent = " CONCURRENT" if concurrent else ""
    1865        target = self.sql(expression, "target")
    1866        target = f" {target}" if target else ""
    1867        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

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

    1869    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1870        if isinstance(expression.this, list):
    1871            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1872        if expression.this:
    1873            modulus = self.sql(expression, "this")
    1874            remainder = self.sql(expression, "expression")
    1875            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1876
    1877        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1878        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1879        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1881    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1882        this = self.sql(expression, "this")
    1883
    1884        for_values_or_default = expression.expression
    1885        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1886            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1887        else:
    1888            for_values_or_default = " DEFAULT"
    1889
    1890        return f"PARTITION OF {this}{for_values_or_default}"

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

    1892    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1893        kind = expression.args.get("kind")
    1894        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1895        for_or_in = expression.args.get("for_or_in")
    1896        for_or_in = f" {for_or_in}" if for_or_in else ""
    1897        lock_type = expression.args.get("lock_type")
    1898        override = " OVERRIDE" if expression.args.get("override") else ""
    1899        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1901    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1902        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1903        statistics = expression.args.get("statistics")
    1904        statistics_sql = ""
    1905        if statistics is not None:
    1906            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1907        return f"{data_sql}{statistics_sql}"

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

    1909    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1910        this = self.sql(expression, "this")
    1911        this = f"HISTORY_TABLE={this}" if this else ""
    1912        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1913        data_consistency = (
    1914            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1915        )
    1916        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1917        retention_period = (
    1918            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1919        )
    1920
    1921        if this:
    1922            on_sql = self.func("ON", this, data_consistency, retention_period)
    1923        else:
    1924            on_sql = "ON" if expression.args.get("on") else "OFF"
    1925
    1926        sql = f"SYSTEM_VERSIONING={on_sql}"
    1927
    1928        return f"WITH({sql})" if expression.args.get("with") else sql

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

    1930    def insert_sql(self, expression: exp.Insert) -> str:
    1931        hint = self.sql(expression, "hint")
    1932        overwrite = expression.args.get("overwrite")
    1933
    1934        if isinstance(expression.this, exp.Directory):
    1935            this = " OVERWRITE" if overwrite else " INTO"
    1936        else:
    1937            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1938
    1939        stored = self.sql(expression, "stored")
    1940        stored = f" {stored}" if stored else ""
    1941        alternative = expression.args.get("alternative")
    1942        alternative = f" OR {alternative}" if alternative else ""
    1943        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1944        is_function = expression.args.get("is_function")
    1945        if is_function:
    1946            this = f"{this} FUNCTION"
    1947        this = f"{this} {self.sql(expression, 'this')}"
    1948
    1949        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1950        where = self.sql(expression, "where")
    1951        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1952        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1953        on_conflict = self.sql(expression, "conflict")
    1954        on_conflict = f" {on_conflict}" if on_conflict else ""
    1955        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1956        returning = self.sql(expression, "returning")
    1957
    1958        if self.RETURNING_END:
    1959            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1960        else:
    1961            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1962
    1963        partition_by = self.sql(expression, "partition")
    1964        partition_by = f" {partition_by}" if partition_by else ""
    1965        settings = self.sql(expression, "settings")
    1966        settings = f" {settings}" if settings else ""
    1967
    1968        source = self.sql(expression, "source")
    1969        source = f"TABLE {source}" if source else ""
    1970
    1971        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1972        return self.prepend_ctes(expression, sql)

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

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

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

    1977    def kill_sql(self, expression: exp.Kill) -> str:
    1978        kind = self.sql(expression, "kind")
    1979        kind = f" {kind}" if kind else ""
    1980        this = self.sql(expression, "this")
    1981        this = f" {this}" if this else ""
    1982        return f"KILL{kind}{this}"

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

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

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

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

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

    1990    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1991        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1992
    1993        constraint = self.sql(expression, "constraint")
    1994        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1995
    1996        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1997        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1998        action = self.sql(expression, "action")
    1999
    2000        expressions = self.expressions(expression, flat=True)
    2001        if expressions:
    2002            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    2003            expressions = f" {set_keyword}{expressions}"
    2004
    2005        where = self.sql(expression, "where")
    2006        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"

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

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

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

    2011    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    2012        fields = self.sql(expression, "fields")
    2013        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    2014        escaped = self.sql(expression, "escaped")
    2015        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    2016        items = self.sql(expression, "collection_items")
    2017        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    2018        keys = self.sql(expression, "map_keys")
    2019        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    2020        lines = self.sql(expression, "lines")
    2021        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    2022        null = self.sql(expression, "null")
    2023        null = f" NULL DEFINED AS {null}" if null else ""
    2024        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    2029    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    2030        this = f"{self.sql(expression, 'this')} INDEX"
    2031        target = self.sql(expression, "target")
    2032        target = f" FOR {target}" if target else ""
    2033        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    2035    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    2036        this = self.sql(expression, "this")
    2037        kind = self.sql(expression, "kind")
    2038        expr = self.sql(expression, "expression")
    2039        return f"{this} ({kind} => {expr})"

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

    2041    def table_parts(self, expression: exp.Table) -> str:
    2042        return ".".join(
    2043            self.sql(part)
    2044            for part in (
    2045                expression.args.get("catalog"),
    2046                expression.args.get("db"),
    2047                expression.args.get("this"),
    2048            )
    2049            if part is not None
    2050        )

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

    2052    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    2053        table = self.table_parts(expression)
    2054        only = "ONLY " if expression.args.get("only") else ""
    2055        partition = self.sql(expression, "partition")
    2056        partition = f" {partition}" if partition else ""
    2057        version = self.sql(expression, "version")
    2058        version = f" {version}" if version else ""
    2059        alias = self.sql(expression, "alias")
    2060        alias = f"{sep}{alias}" if alias else ""
    2061
    2062        sample = self.sql(expression, "sample")
    2063        if self.dialect.ALIAS_POST_TABLESAMPLE:
    2064            sample_pre_alias = sample
    2065            sample_post_alias = ""
    2066        else:
    2067            sample_pre_alias = ""
    2068            sample_post_alias = sample
    2069
    2070        hints = self.expressions(expression, key="hints", sep=" ")
    2071        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    2072        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2073        joins = self.indent(
    2074            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    2075        )
    2076        laterals = self.expressions(expression, key="laterals", sep="")
    2077
    2078        file_format = self.sql(expression, "format")
    2079        if file_format:
    2080            pattern = self.sql(expression, "pattern")
    2081            pattern = f", PATTERN => {pattern}" if pattern else ""
    2082            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    2083
    2084        ordinality = expression.args.get("ordinality") or ""
    2085        if ordinality:
    2086            ordinality = f" WITH ORDINALITY{alias}"
    2087            alias = ""
    2088
    2089        when = self.sql(expression, "when")
    2090        if when:
    2091            table = f"{table} {when}"
    2092
    2093        changes = self.sql(expression, "changes")
    2094        changes = f" {changes}" if changes else ""
    2095
    2096        rows_from = self.expressions(expression, key="rows_from")
    2097        if rows_from:
    2098            table = f"ROWS FROM {self.wrap(rows_from)}"
    2099
    2100        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

    2102    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
    2103        table = self.func("TABLE", expression.this)
    2104        alias = self.sql(expression, "alias")
    2105        alias = f" AS {alias}" if alias else ""
    2106        sample = self.sql(expression, "sample")
    2107        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2108        joins = self.indent(
    2109            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    2110        )
    2111        return f"{table}{alias}{pivots}{sample}{joins}"

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

    2113    def tablesample_sql(
    2114        self,
    2115        expression: exp.TableSample,
    2116        tablesample_keyword: t.Optional[str] = None,
    2117    ) -> str:
    2118        method = self.sql(expression, "method")
    2119        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    2120        numerator = self.sql(expression, "bucket_numerator")
    2121        denominator = self.sql(expression, "bucket_denominator")
    2122        field = self.sql(expression, "bucket_field")
    2123        field = f" ON {field}" if field else ""
    2124        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    2125        seed = self.sql(expression, "seed")
    2126        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    2127
    2128        size = self.sql(expression, "size")
    2129        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    2130            size = f"{size} ROWS"
    2131
    2132        percent = self.sql(expression, "percent")
    2133        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    2134            percent = f"{percent} PERCENT"
    2135
    2136        expr = f"{bucket}{percent}{size}"
    2137        if self.TABLESAMPLE_REQUIRES_PARENS:
    2138            expr = f"({expr})"
    2139
    2140        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"

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

    2142    def pivot_sql(self, expression: exp.Pivot) -> str:
    2143        expressions = self.expressions(expression, flat=True)
    2144        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2145
    2146        group = self.sql(expression, "group")
    2147
    2148        if expression.this:
    2149            this = self.sql(expression, "this")
    2150            if not expressions:
    2151                return f"UNPIVOT {this}"
    2152
    2153            on = f"{self.seg('ON')} {expressions}"
    2154            into = self.sql(expression, "into")
    2155            into = f"{self.seg('INTO')} {into}" if into else ""
    2156            using = self.expressions(expression, key="using", flat=True)
    2157            using = f"{self.seg('USING')} {using}" if using else ""
    2158            return f"{direction} {this}{on}{into}{using}{group}"
    2159
    2160        alias = self.sql(expression, "alias")
    2161        alias = f" AS {alias}" if alias else ""
    2162
    2163        fields = self.expressions(
    2164            expression,
    2165            "fields",
    2166            sep=" ",
    2167            dynamic=True,
    2168            new_line=True,
    2169            skip_first=True,
    2170            skip_last=True,
    2171        )
    2172
    2173        include_nulls = expression.args.get("include_nulls")
    2174        if include_nulls is not None:
    2175            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2176        else:
    2177            nulls = ""
    2178
    2179        default_on_null = self.sql(expression, "default_on_null")
    2180        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2181        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

    2183    def version_sql(self, expression: exp.Version) -> str:
    2184        this = f"FOR {expression.name}"
    2185        kind = expression.text("kind")
    2186        expr = self.sql(expression, "expression")
    2187        return f"{this} {kind} {expr}"

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

    2189    def tuple_sql(self, expression: exp.Tuple) -> str:
    2190        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

    2192    def update_sql(self, expression: exp.Update) -> str:
    2193        this = self.sql(expression, "this")
    2194        set_sql = self.expressions(expression, flat=True)
    2195        from_sql = self.sql(expression, "from")
    2196        where_sql = self.sql(expression, "where")
    2197        returning = self.sql(expression, "returning")
    2198        order = self.sql(expression, "order")
    2199        limit = self.sql(expression, "limit")
    2200        if self.RETURNING_END:
    2201            expression_sql = f"{from_sql}{where_sql}{returning}"
    2202        else:
    2203            expression_sql = f"{returning}{from_sql}{where_sql}"
    2204        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2205        return self.prepend_ctes(expression, sql)

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

    2207    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2208        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2209
    2210        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2211        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2212            args = self.expressions(expression)
    2213            alias = self.sql(expression, "alias")
    2214            values = f"VALUES{self.seg('')}{args}"
    2215            values = (
    2216                f"({values})"
    2217                if self.WRAP_DERIVED_VALUES
    2218                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2219                else values
    2220            )
    2221            return f"{values} AS {alias}" if alias else values
    2222
    2223        # Converts `VALUES...` expression into a series of select unions.
    2224        alias_node = expression.args.get("alias")
    2225        column_names = alias_node and alias_node.columns
    2226
    2227        selects: t.List[exp.Query] = []
    2228
    2229        for i, tup in enumerate(expression.expressions):
    2230            row = tup.expressions
    2231
    2232            if i == 0 and column_names:
    2233                row = [
    2234                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2235                ]
    2236
    2237            selects.append(exp.Select(expressions=row))
    2238
    2239        if self.pretty:
    2240            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2241            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2242            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2243            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2244            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2245
    2246        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2247        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2248        return f"({unions}){alias}"

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

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

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

    2253    @unsupported_args("expressions")
    2254    def into_sql(self, expression: exp.Into) -> str:
    2255        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2256        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2257        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"

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

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

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

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

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

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

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

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

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

    2274    def group_sql(self, expression: exp.Group) -> str:
    2275        group_by_all = expression.args.get("all")
    2276        if group_by_all is True:
    2277            modifier = " ALL"
    2278        elif group_by_all is False:
    2279            modifier = " DISTINCT"
    2280        else:
    2281            modifier = ""
    2282
    2283        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2284
    2285        grouping_sets = self.expressions(expression, key="grouping_sets")
    2286        cube = self.expressions(expression, key="cube")
    2287        rollup = self.expressions(expression, key="rollup")
    2288
    2289        groupings = csv(
    2290            self.seg(grouping_sets) if grouping_sets else "",
    2291            self.seg(cube) if cube else "",
    2292            self.seg(rollup) if rollup else "",
    2293            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2294            sep=self.GROUPINGS_SEP,
    2295        )
    2296
    2297        if (
    2298            expression.expressions
    2299            and groupings
    2300            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2301        ):
    2302            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2303
    2304        return f"{group_by}{groupings}"

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

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

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

    2310    def connect_sql(self, expression: exp.Connect) -> str:
    2311        start = self.sql(expression, "start")
    2312        start = self.seg(f"START WITH {start}") if start else ""
    2313        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2314        connect = self.sql(expression, "connect")
    2315        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2316        return start + connect

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

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

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

    2321    def join_sql(self, expression: exp.Join) -> str:
    2322        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2323            side = None
    2324        else:
    2325            side = expression.side
    2326
    2327        op_sql = " ".join(
    2328            op
    2329            for op in (
    2330                expression.method,
    2331                "GLOBAL" if expression.args.get("global") else None,
    2332                side,
    2333                expression.kind,
    2334                expression.hint if self.JOIN_HINTS else None,
    2335            )
    2336            if op
    2337        )
    2338        match_cond = self.sql(expression, "match_condition")
    2339        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2340        on_sql = self.sql(expression, "on")
    2341        using = expression.args.get("using")
    2342
    2343        if not on_sql and using:
    2344            on_sql = csv(*(self.sql(column) for column in using))
    2345
    2346        this = expression.this
    2347        this_sql = self.sql(this)
    2348
    2349        exprs = self.expressions(expression)
    2350        if exprs:
    2351            this_sql = f"{this_sql},{self.seg(exprs)}"
    2352
    2353        if on_sql:
    2354            on_sql = self.indent(on_sql, skip_first=True)
    2355            space = self.seg(" " * self.pad) if self.pretty else " "
    2356            if using:
    2357                on_sql = f"{space}USING ({on_sql})"
    2358            else:
    2359                on_sql = f"{space}ON {on_sql}"
    2360        elif not op_sql:
    2361            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2362                return f" {this_sql}"
    2363
    2364            return f", {this_sql}"
    2365
    2366        if op_sql != "STRAIGHT_JOIN":
    2367            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2368
    2369        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2370        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}{pivots}"

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

    2372    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->", wrap: bool = True) -> str:
    2373        args = self.expressions(expression, flat=True)
    2374        args = f"({args})" if wrap and len(args.split(",")) > 1 else args
    2375        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"

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

    2377    def lateral_op(self, expression: exp.Lateral) -> str:
    2378        cross_apply = expression.args.get("cross_apply")
    2379
    2380        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2381        if cross_apply is True:
    2382            op = "INNER JOIN "
    2383        elif cross_apply is False:
    2384            op = "LEFT JOIN "
    2385        else:
    2386            op = ""
    2387
    2388        return f"{op}LATERAL"

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

    2390    def lateral_sql(self, expression: exp.Lateral) -> str:
    2391        this = self.sql(expression, "this")
    2392
    2393        if expression.args.get("view"):
    2394            alias = expression.args["alias"]
    2395            columns = self.expressions(alias, key="columns", flat=True)
    2396            table = f" {alias.name}" if alias.name else ""
    2397            columns = f" AS {columns}" if columns else ""
    2398            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2399            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2400
    2401        alias = self.sql(expression, "alias")
    2402        alias = f" AS {alias}" if alias else ""
    2403
    2404        ordinality = expression.args.get("ordinality") or ""
    2405        if ordinality:
    2406            ordinality = f" WITH ORDINALITY{alias}"
    2407            alias = ""
    2408
    2409        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"

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

    2411    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2412        this = self.sql(expression, "this")
    2413
    2414        args = [
    2415            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2416            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2417            if e
    2418        ]
    2419
    2420        args_sql = ", ".join(self.sql(e) for e in args)
    2421        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2422        expressions = self.expressions(expression, flat=True)
    2423        limit_options = self.sql(expression, "limit_options")
    2424        expressions = f" BY {expressions}" if expressions else ""
    2425
    2426        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

    2428    def offset_sql(self, expression: exp.Offset) -> str:
    2429        this = self.sql(expression, "this")
    2430        value = expression.expression
    2431        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2432        expressions = self.expressions(expression, flat=True)
    2433        expressions = f" BY {expressions}" if expressions else ""
    2434        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    2436    def setitem_sql(self, expression: exp.SetItem) -> str:
    2437        kind = self.sql(expression, "kind")
    2438        kind = f"{kind} " if kind else ""
    2439        this = self.sql(expression, "this")
    2440        expressions = self.expressions(expression)
    2441        collate = self.sql(expression, "collate")
    2442        collate = f" COLLATE {collate}" if collate else ""
    2443        global_ = "GLOBAL " if expression.args.get("global") else ""
    2444        return f"{global_}{kind}{this}{expressions}{collate}"

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

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

def queryband_sql(self, expression: sqlglot.expressions.QueryBand) -> str: View Source

    2451    def queryband_sql(self, expression: exp.QueryBand) -> str:
    2452        this = self.sql(expression, "this")
    2453        update = " UPDATE" if expression.args.get("update") else ""
    2454        scope = self.sql(expression, "scope")
    2455        scope = f" FOR {scope}" if scope else ""
    2456
    2457        return f"QUERY_BAND = {this}{update}{scope}"

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

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

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

    2462    def lock_sql(self, expression: exp.Lock) -> str:
    2463        if not self.LOCKING_READS_SUPPORTED:
    2464            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2465            return ""
    2466
    2467        update = expression.args["update"]
    2468        key = expression.args.get("key")
    2469        if update:
    2470            lock_type = "FOR NO KEY UPDATE" if key else "FOR UPDATE"
    2471        else:
    2472            lock_type = "FOR KEY SHARE" if key else "FOR SHARE"
    2473        expressions = self.expressions(expression, flat=True)
    2474        expressions = f" OF {expressions}" if expressions else ""
    2475        wait = expression.args.get("wait")
    2476
    2477        if wait is not None:
    2478            if isinstance(wait, exp.Literal):
    2479                wait = f" WAIT {self.sql(wait)}"
    2480            else:
    2481                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2482
    2483        return f"{lock_type}{expressions}{wait or ''}"

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

    2485    def literal_sql(self, expression: exp.Literal) -> str:
    2486        text = expression.this or ""
    2487        if expression.is_string:
    2488            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2489        return text

def escape_str( self, text: str, escape_backslash: bool = True, delimiter: Optional[str] = None, escaped_delimiter: Optional[str] = None) -> str: View Source

    2491    def escape_str(
    2492        self,
    2493        text: str,
    2494        escape_backslash: bool = True,
    2495        delimiter: t.Optional[str] = None,
    2496        escaped_delimiter: t.Optional[str] = None,
    2497    ) -> str:
    2498        if self.dialect.ESCAPED_SEQUENCES:
    2499            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2500            text = "".join(
    2501                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2502            )
    2503
    2504        delimiter = delimiter or self.dialect.QUOTE_END
    2505        escaped_delimiter = escaped_delimiter or self._escaped_quote_end
    2506
    2507        return self._replace_line_breaks(text).replace(delimiter, escaped_delimiter)

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

    2509    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2510        local = " LOCAL" if expression.args.get("local") else ""
    2511        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2512        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2513        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2514        partition = self.sql(expression, "partition")
    2515        partition = f" {partition}" if partition else ""
    2516        input_format = self.sql(expression, "input_format")
    2517        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2518        serde = self.sql(expression, "serde")
    2519        serde = f" SERDE {serde}" if serde else ""
    2520        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    2522    def null_sql(self, *_) -> str:
    2523        return "NULL"

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

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

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

    2528    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2529        this = self.sql(expression, "this")
    2530        this = f"{this} " if this else this
    2531        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2532        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

    2534    def withfill_sql(self, expression: exp.WithFill) -> str:
    2535        from_sql = self.sql(expression, "from")
    2536        from_sql = f" FROM {from_sql}" if from_sql else ""
    2537        to_sql = self.sql(expression, "to")
    2538        to_sql = f" TO {to_sql}" if to_sql else ""
    2539        step_sql = self.sql(expression, "step")
    2540        step_sql = f" STEP {step_sql}" if step_sql else ""
    2541        interpolated_values = [
    2542            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2543            if isinstance(e, exp.Alias)
    2544            else self.sql(e, "this")
    2545            for e in expression.args.get("interpolate") or []
    2546        ]
    2547        interpolate = (
    2548            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2549        )
    2550        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"

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

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

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

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

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

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

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

    2561    def ordered_sql(self, expression: exp.Ordered) -> str:
    2562        desc = expression.args.get("desc")
    2563        asc = not desc
    2564
    2565        nulls_first = expression.args.get("nulls_first")
    2566        nulls_last = not nulls_first
    2567        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2568        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2569        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2570
    2571        this = self.sql(expression, "this")
    2572
    2573        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2574        nulls_sort_change = ""
    2575        if nulls_first and (
    2576            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2577        ):
    2578            nulls_sort_change = " NULLS FIRST"
    2579        elif (
    2580            nulls_last
    2581            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2582            and not nulls_are_last
    2583        ):
    2584            nulls_sort_change = " NULLS LAST"
    2585
    2586        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2587        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2588            window = expression.find_ancestor(exp.Window, exp.Select)
    2589            if isinstance(window, exp.Window) and window.args.get("spec"):
    2590                self.unsupported(
    2591                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2592                )
    2593                nulls_sort_change = ""
    2594            elif self.NULL_ORDERING_SUPPORTED is False and (
    2595                (asc and nulls_sort_change == " NULLS LAST")
    2596                or (desc and nulls_sort_change == " NULLS FIRST")
    2597            ):
    2598                # BigQuery does not allow these ordering/nulls combinations when used under
    2599                # an aggregation func or under a window containing one
    2600                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2601
    2602                if isinstance(ancestor, exp.Window):
    2603                    ancestor = ancestor.this
    2604                if isinstance(ancestor, exp.AggFunc):
    2605                    self.unsupported(
    2606                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2607                    )
    2608                    nulls_sort_change = ""
    2609            elif self.NULL_ORDERING_SUPPORTED is None:
    2610                if expression.this.is_int:
    2611                    self.unsupported(
    2612                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2613                    )
    2614                elif not isinstance(expression.this, exp.Rand):
    2615                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2616                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2617                nulls_sort_change = ""
    2618
    2619        with_fill = self.sql(expression, "with_fill")
    2620        with_fill = f" {with_fill}" if with_fill else ""
    2621
    2622        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

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

    2624    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2625        window_frame = self.sql(expression, "window_frame")
    2626        window_frame = f"{window_frame} " if window_frame else ""
    2627
    2628        this = self.sql(expression, "this")
    2629
    2630        return f"{window_frame}{this}"

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

    2632    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2633        partition = self.partition_by_sql(expression)
    2634        order = self.sql(expression, "order")
    2635        measures = self.expressions(expression, key="measures")
    2636        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2637        rows = self.sql(expression, "rows")
    2638        rows = self.seg(rows) if rows else ""
    2639        after = self.sql(expression, "after")
    2640        after = self.seg(after) if after else ""
    2641        pattern = self.sql(expression, "pattern")
    2642        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2643        definition_sqls = [
    2644            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2645            for definition in expression.args.get("define", [])
    2646        ]
    2647        definitions = self.expressions(sqls=definition_sqls)
    2648        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2649        body = "".join(
    2650            (
    2651                partition,
    2652                order,
    2653                measures,
    2654                rows,
    2655                after,
    2656                pattern,
    2657                define,
    2658            )
    2659        )
    2660        alias = self.sql(expression, "alias")
    2661        alias = f" {alias}" if alias else ""
    2662        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2664    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2665        limit = expression.args.get("limit")
    2666
    2667        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2668            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2669        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2670            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2671
    2672        return csv(
    2673            *sqls,
    2674            *[self.sql(join) for join in expression.args.get("joins") or []],
    2675            self.sql(expression, "match"),
    2676            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2677            self.sql(expression, "prewhere"),
    2678            self.sql(expression, "where"),
    2679            self.sql(expression, "connect"),
    2680            self.sql(expression, "group"),
    2681            self.sql(expression, "having"),
    2682            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2683            self.sql(expression, "order"),
    2684            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2685            *self.after_limit_modifiers(expression),
    2686            self.options_modifier(expression),
    2687            self.for_modifiers(expression),
    2688            sep="",
    2689        )

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

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

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

    2695    def for_modifiers(self, expression: exp.Expression) -> str:
    2696        for_modifiers = self.expressions(expression, key="for")
    2697        return f"{self.sep()}FOR XML{self.seg(for_modifiers)}" if for_modifiers else ""

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

    2699    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2700        self.unsupported("Unsupported query option.")
    2701        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

    2703    def offset_limit_modifiers(
    2704        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2705    ) -> t.List[str]:
    2706        return [
    2707            self.sql(expression, "offset") if fetch else self.sql(limit),
    2708            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2709        ]

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

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

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

    2716    def select_sql(self, expression: exp.Select) -> str:
    2717        into = expression.args.get("into")
    2718        if not self.SUPPORTS_SELECT_INTO and into:
    2719            into.pop()
    2720
    2721        hint = self.sql(expression, "hint")
    2722        distinct = self.sql(expression, "distinct")
    2723        distinct = f" {distinct}" if distinct else ""
    2724        kind = self.sql(expression, "kind")
    2725
    2726        limit = expression.args.get("limit")
    2727        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2728            top = self.limit_sql(limit, top=True)
    2729            limit.pop()
    2730        else:
    2731            top = ""
    2732
    2733        expressions = self.expressions(expression)
    2734
    2735        if kind:
    2736            if kind in self.SELECT_KINDS:
    2737                kind = f" AS {kind}"
    2738            else:
    2739                if kind == "STRUCT":
    2740                    expressions = self.expressions(
    2741                        sqls=[
    2742                            self.sql(
    2743                                exp.Struct(
    2744                                    expressions=[
    2745                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2746                                        if isinstance(e, exp.Alias)
    2747                                        else e
    2748                                        for e in expression.expressions
    2749                                    ]
    2750                                )
    2751                            )
    2752                        ]
    2753                    )
    2754                kind = ""
    2755
    2756        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2757        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2758
    2759        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2760        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2761        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2762        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2763        sql = self.query_modifiers(
    2764            expression,
    2765            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2766            self.sql(expression, "into", comment=False),
    2767            self.sql(expression, "from", comment=False),
    2768        )
    2769
    2770        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2771        if expression.args.get("with"):
    2772            sql = self.maybe_comment(sql, expression)
    2773            expression.pop_comments()
    2774
    2775        sql = self.prepend_ctes(expression, sql)
    2776
    2777        if not self.SUPPORTS_SELECT_INTO and into:
    2778            if into.args.get("temporary"):
    2779                table_kind = " TEMPORARY"
    2780            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2781                table_kind = " UNLOGGED"
    2782            else:
    2783                table_kind = ""
    2784            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2785
    2786        return sql

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

    2788    def schema_sql(self, expression: exp.Schema) -> str:
    2789        this = self.sql(expression, "this")
    2790        sql = self.schema_columns_sql(expression)
    2791        return f"{this} {sql}" if this and sql else this or sql

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

    2793    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2794        if expression.expressions:
    2795            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2796        return ""

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

    2798    def star_sql(self, expression: exp.Star) -> str:
    2799        except_ = self.expressions(expression, key="except", flat=True)
    2800        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2801        replace = self.expressions(expression, key="replace", flat=True)
    2802        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2803        rename = self.expressions(expression, key="rename", flat=True)
    2804        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2805        return f"*{except_}{replace}{rename}"

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

    2807    def parameter_sql(self, expression: exp.Parameter) -> str:
    2808        this = self.sql(expression, "this")
    2809        return f"{self.PARAMETER_TOKEN}{this}"

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

    2811    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2812        this = self.sql(expression, "this")
    2813        kind = expression.text("kind")
    2814        if kind:
    2815            kind = f"{kind}."
    2816        return f"@@{kind}{this}"

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

    2818    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2819        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

    2821    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2822        alias = self.sql(expression, "alias")
    2823        alias = f"{sep}{alias}" if alias else ""
    2824        sample = self.sql(expression, "sample")
    2825        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2826            alias = f"{sample}{alias}"
    2827
    2828            # Set to None so it's not generated again by self.query_modifiers()
    2829            expression.set("sample", None)
    2830
    2831        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2832        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2833        return self.prepend_ctes(expression, sql)

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

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

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

    2839    def unnest_sql(self, expression: exp.Unnest) -> str:
    2840        args = self.expressions(expression, flat=True)
    2841
    2842        alias = expression.args.get("alias")
    2843        offset = expression.args.get("offset")
    2844
    2845        if self.UNNEST_WITH_ORDINALITY:
    2846            if alias and isinstance(offset, exp.Expression):
    2847                alias.append("columns", offset)
    2848
    2849        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2850            columns = alias.columns
    2851            alias = self.sql(columns[0]) if columns else ""
    2852        else:
    2853            alias = self.sql(alias)
    2854
    2855        alias = f" AS {alias}" if alias else alias
    2856        if self.UNNEST_WITH_ORDINALITY:
    2857            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2858        else:
    2859            if isinstance(offset, exp.Expression):
    2860                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2861            elif offset:
    2862                suffix = f"{alias} WITH OFFSET"
    2863            else:
    2864                suffix = alias
    2865
    2866        return f"UNNEST({args}){suffix}"

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

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

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

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

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

    2875    def window_sql(self, expression: exp.Window) -> str:
    2876        this = self.sql(expression, "this")
    2877        partition = self.partition_by_sql(expression)
    2878        order = expression.args.get("order")
    2879        order = self.order_sql(order, flat=True) if order else ""
    2880        spec = self.sql(expression, "spec")
    2881        alias = self.sql(expression, "alias")
    2882        over = self.sql(expression, "over") or "OVER"
    2883
    2884        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2885
    2886        first = expression.args.get("first")
    2887        if first is None:
    2888            first = ""
    2889        else:
    2890            first = "FIRST" if first else "LAST"
    2891
    2892        if not partition and not order and not spec and alias:
    2893            return f"{this} {alias}"
    2894
    2895        args = self.format_args(
    2896            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
    2897        )
    2898        return f"{this} ({args})"

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

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

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

    2904    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2905        kind = self.sql(expression, "kind")
    2906        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2907        end = (
    2908            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2909            or "CURRENT ROW"
    2910        )
    2911
    2912        window_spec = f"{kind} BETWEEN {start} AND {end}"
    2913
    2914        exclude = self.sql(expression, "exclude")
    2915        if exclude:
    2916            if self.SUPPORTS_WINDOW_EXCLUDE:
    2917                window_spec += f" EXCLUDE {exclude}"
    2918            else:
    2919                self.unsupported("EXCLUDE clause is not supported in the WINDOW clause")
    2920
    2921        return window_spec

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

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

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

    2928    def between_sql(self, expression: exp.Between) -> str:
    2929        this = self.sql(expression, "this")
    2930        low = self.sql(expression, "low")
    2931        high = self.sql(expression, "high")
    2932        symmetric = expression.args.get("symmetric")
    2933
    2934        if symmetric and not self.SUPPORTS_BETWEEN_FLAGS:
    2935            return f"({this} BETWEEN {low} AND {high} OR {this} BETWEEN {high} AND {low})"
    2936
    2937        flag = (
    2938            " SYMMETRIC"
    2939            if symmetric
    2940            else " ASYMMETRIC"
    2941            if symmetric is False and self.SUPPORTS_BETWEEN_FLAGS
    2942            else ""  # silently drop ASYMMETRIC – semantics identical
    2943        )
    2944        return f"{this} BETWEEN{flag} {low} AND {high}"

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

    2946    def bracket_offset_expressions(
    2947        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2948    ) -> t.List[exp.Expression]:
    2949        return apply_index_offset(
    2950            expression.this,
    2951            expression.expressions,
    2952            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2953            dialect=self.dialect,
    2954        )

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

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

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

    2961    def all_sql(self, expression: exp.All) -> str:
    2962        this = self.sql(expression, "this")
    2963        if not isinstance(expression.this, (exp.Tuple, exp.Paren)):
    2964            this = self.wrap(this)
    2965        return f"ALL {this}"

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

    2967    def any_sql(self, expression: exp.Any) -> str:
    2968        this = self.sql(expression, "this")
    2969        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2970            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2971                this = self.wrap(this)
    2972            return f"ANY{this}"
    2973        return f"ANY {this}"

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

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

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

    2978    def case_sql(self, expression: exp.Case) -> str:
    2979        this = self.sql(expression, "this")
    2980        statements = [f"CASE {this}" if this else "CASE"]
    2981
    2982        for e in expression.args["ifs"]:
    2983            statements.append(f"WHEN {self.sql(e, 'this')}")
    2984            statements.append(f"THEN {self.sql(e, 'true')}")
    2985
    2986        default = self.sql(expression, "default")
    2987
    2988        if default:
    2989            statements.append(f"ELSE {default}")
    2990
    2991        statements.append("END")
    2992
    2993        if self.pretty and self.too_wide(statements):
    2994            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2995
    2996        return " ".join(statements)

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

    2998    def constraint_sql(self, expression: exp.Constraint) -> str:
    2999        this = self.sql(expression, "this")
    3000        expressions = self.expressions(expression, flat=True)
    3001        return f"CONSTRAINT {this} {expressions}"

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

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

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

    3008    def extract_sql(self, expression: exp.Extract) -> str:
    3009        from sqlglot.dialects.dialect import map_date_part
    3010
    3011        this = (
    3012            map_date_part(expression.this, self.dialect)
    3013            if self.NORMALIZE_EXTRACT_DATE_PARTS
    3014            else expression.this
    3015        )
    3016        this_sql = self.sql(this) if self.EXTRACT_ALLOWS_QUOTES else this.name
    3017        expression_sql = self.sql(expression, "expression")
    3018
    3019        return f"EXTRACT({this_sql} FROM {expression_sql})"

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

    3021    def trim_sql(self, expression: exp.Trim) -> str:
    3022        trim_type = self.sql(expression, "position")
    3023
    3024        if trim_type == "LEADING":
    3025            func_name = "LTRIM"
    3026        elif trim_type == "TRAILING":
    3027            func_name = "RTRIM"
    3028        else:
    3029            func_name = "TRIM"
    3030
    3031        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

    3033    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    3034        args = expression.expressions
    3035        if isinstance(expression, exp.ConcatWs):
    3036            args = args[1:]  # Skip the delimiter
    3037
    3038        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3039            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    3040
    3041        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    3042
    3043            def _wrap_with_coalesce(e: exp.Expression) -> exp.Expression:
    3044                if not e.type:
    3045                    from sqlglot.optimizer.annotate_types import annotate_types
    3046
    3047                    e = annotate_types(e, dialect=self.dialect)
    3048
    3049                if e.is_string or e.is_type(exp.DataType.Type.ARRAY):
    3050                    return e
    3051
    3052                return exp.func("coalesce", e, exp.Literal.string(""))
    3053
    3054            args = [_wrap_with_coalesce(e) for e in args]
    3055
    3056        return args

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

    3058    def concat_sql(self, expression: exp.Concat) -> str:
    3059        expressions = self.convert_concat_args(expression)
    3060
    3061        # Some dialects don't allow a single-argument CONCAT call
    3062        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    3063            return self.sql(expressions[0])
    3064
    3065        return self.func("CONCAT", *expressions)

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

    3067    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    3068        return self.func(
    3069            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    3070        )

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

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

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

    3076    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    3077        expressions = self.expressions(expression, flat=True)
    3078        expressions = f" ({expressions})" if expressions else ""
    3079        reference = self.sql(expression, "reference")
    3080        reference = f" {reference}" if reference else ""
    3081        delete = self.sql(expression, "delete")
    3082        delete = f" ON DELETE {delete}" if delete else ""
    3083        update = self.sql(expression, "update")
    3084        update = f" ON UPDATE {update}" if update else ""
    3085        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3086        options = f" {options}" if options else ""
    3087        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"

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

    3089    def primarykey_sql(self, expression: exp.PrimaryKey) -> str:
    3090        expressions = self.expressions(expression, flat=True)
    3091        include = self.sql(expression, "include")
    3092        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3093        options = f" {options}" if options else ""
    3094        return f"PRIMARY KEY ({expressions}){include}{options}"

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

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

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

    3099    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    3100        if self.MATCH_AGAINST_TABLE_PREFIX:
    3101            expressions = []
    3102            for expr in expression.expressions:
    3103                if isinstance(expr, exp.Table):
    3104                    expressions.append(f"TABLE {self.sql(expr)}")
    3105                else:
    3106                    expressions.append(expr)
    3107        else:
    3108            expressions = expression.expressions
    3109
    3110        modifier = expression.args.get("modifier")
    3111        modifier = f" {modifier}" if modifier else ""
    3112        return (
    3113            f"{self.func('MATCH', *expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    3114        )

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

    3116    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    3117        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

    3119    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    3120        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    3121
    3122        if expression.args.get("escape"):
    3123            path = self.escape_str(path)
    3124
    3125        if self.QUOTE_JSON_PATH:
    3126            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    3127
    3128        return path

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

    3130    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    3131        if isinstance(expression, exp.JSONPathPart):
    3132            transform = self.TRANSFORMS.get(expression.__class__)
    3133            if not callable(transform):
    3134                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    3135                return ""
    3136
    3137            return transform(self, expression)
    3138
    3139        if isinstance(expression, int):
    3140            return str(expression)
    3141
    3142        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    3143            escaped = expression.replace("'", "\\'")
    3144            escaped = f"\\'{expression}\\'"
    3145        else:
    3146            escaped = expression.replace('"', '\\"')
    3147            escaped = f'"{escaped}"'
    3148
    3149        return escaped

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

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

def formatphrase_sql(self, expression: sqlglot.expressions.FormatPhrase) -> str: View Source

    3154    def formatphrase_sql(self, expression: exp.FormatPhrase) -> str:
    3155        # Output the Teradata column FORMAT override.
    3156        # https://docs.teradata.com/r/Enterprise_IntelliFlex_VMware/SQL-Data-Types-and-Literals/Data-Type-Formats-and-Format-Phrases/FORMAT
    3157        this = self.sql(expression, "this")
    3158        fmt = self.sql(expression, "format")
    3159        return f"{this} (FORMAT {fmt})"

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

    3161    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    3162        null_handling = expression.args.get("null_handling")
    3163        null_handling = f" {null_handling}" if null_handling else ""
    3164
    3165        unique_keys = expression.args.get("unique_keys")
    3166        if unique_keys is not None:
    3167            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    3168        else:
    3169            unique_keys = ""
    3170
    3171        return_type = self.sql(expression, "return_type")
    3172        return_type = f" RETURNING {return_type}" if return_type else ""
    3173        encoding = self.sql(expression, "encoding")
    3174        encoding = f" ENCODING {encoding}" if encoding else ""
    3175
    3176        return self.func(
    3177            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    3178            *expression.expressions,
    3179            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    3180        )

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

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

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

    3185    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    3186        null_handling = expression.args.get("null_handling")
    3187        null_handling = f" {null_handling}" if null_handling else ""
    3188        return_type = self.sql(expression, "return_type")
    3189        return_type = f" RETURNING {return_type}" if return_type else ""
    3190        strict = " STRICT" if expression.args.get("strict") else ""
    3191        return self.func(
    3192            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    3193        )

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

    3195    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    3196        this = self.sql(expression, "this")
    3197        order = self.sql(expression, "order")
    3198        null_handling = expression.args.get("null_handling")
    3199        null_handling = f" {null_handling}" if null_handling else ""
    3200        return_type = self.sql(expression, "return_type")
    3201        return_type = f" RETURNING {return_type}" if return_type else ""
    3202        strict = " STRICT" if expression.args.get("strict") else ""
    3203        return self.func(
    3204            "JSON_ARRAYAGG",
    3205            this,
    3206            suffix=f"{order}{null_handling}{return_type}{strict})",
    3207        )

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

    3209    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    3210        path = self.sql(expression, "path")
    3211        path = f" PATH {path}" if path else ""
    3212        nested_schema = self.sql(expression, "nested_schema")
    3213
    3214        if nested_schema:
    3215            return f"NESTED{path} {nested_schema}"
    3216
    3217        this = self.sql(expression, "this")
    3218        kind = self.sql(expression, "kind")
    3219        kind = f" {kind}" if kind else ""
    3220        return f"{this}{kind}{path}"

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

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

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

    3225    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    3226        this = self.sql(expression, "this")
    3227        path = self.sql(expression, "path")
    3228        path = f", {path}" if path else ""
    3229        error_handling = expression.args.get("error_handling")
    3230        error_handling = f" {error_handling}" if error_handling else ""
    3231        empty_handling = expression.args.get("empty_handling")
    3232        empty_handling = f" {empty_handling}" if empty_handling else ""
    3233        schema = self.sql(expression, "schema")
    3234        return self.func(
    3235            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    3236        )

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

    3238    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    3239        this = self.sql(expression, "this")
    3240        kind = self.sql(expression, "kind")
    3241        path = self.sql(expression, "path")
    3242        path = f" {path}" if path else ""
    3243        as_json = " AS JSON" if expression.args.get("as_json") else ""
    3244        return f"{this} {kind}{path}{as_json}"

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

    3246    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    3247        this = self.sql(expression, "this")
    3248        path = self.sql(expression, "path")
    3249        path = f", {path}" if path else ""
    3250        expressions = self.expressions(expression)
    3251        with_ = (
    3252            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    3253            if expressions
    3254            else ""
    3255        )
    3256        return f"OPENJSON({this}{path}){with_}"

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

    3258    def in_sql(self, expression: exp.In) -> str:
    3259        query = expression.args.get("query")
    3260        unnest = expression.args.get("unnest")
    3261        field = expression.args.get("field")
    3262        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3263
    3264        if query:
    3265            in_sql = self.sql(query)
    3266        elif unnest:
    3267            in_sql = self.in_unnest_op(unnest)
    3268        elif field:
    3269            in_sql = self.sql(field)
    3270        else:
    3271            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    3272
    3273        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    3278    def interval_sql(self, expression: exp.Interval) -> str:
    3279        unit_expression = expression.args.get("unit")
    3280        unit = self.sql(unit_expression) if unit_expression else ""
    3281        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3282            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3283        unit = f" {unit}" if unit else ""
    3284
    3285        if self.SINGLE_STRING_INTERVAL:
    3286            this = expression.this.name if expression.this else ""
    3287            if this:
    3288                if unit_expression and isinstance(unit_expression, exp.IntervalSpan):
    3289                    return f"INTERVAL '{this}'{unit}"
    3290                return f"INTERVAL '{this}{unit}'"
    3291            return f"INTERVAL{unit}"
    3292
    3293        this = self.sql(expression, "this")
    3294        if this:
    3295            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3296            this = f" {this}" if unwrapped else f" ({this})"
    3297
    3298        return f"INTERVAL{this}{unit}"

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

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

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

    3303    def reference_sql(self, expression: exp.Reference) -> str:
    3304        this = self.sql(expression, "this")
    3305        expressions = self.expressions(expression, flat=True)
    3306        expressions = f"({expressions})" if expressions else ""
    3307        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3308        options = f" {options}" if options else ""
    3309        return f"REFERENCES {this}{expressions}{options}"

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

    3311    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3312        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3313        parent = expression.parent
    3314        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3315        return self.func(
    3316            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3317        )

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

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

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

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

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

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

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

    3332    def alias_sql(self, expression: exp.Alias) -> str:
    3333        alias = self.sql(expression, "alias")
    3334        alias = f" AS {alias}" if alias else ""
    3335        return f"{self.sql(expression, 'this')}{alias}"

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

    3337    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3338        alias = expression.args["alias"]
    3339
    3340        parent = expression.parent
    3341        pivot = parent and parent.parent
    3342
    3343        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
    3344            identifier_alias = isinstance(alias, exp.Identifier)
    3345            literal_alias = isinstance(alias, exp.Literal)
    3346
    3347            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3348                alias.replace(exp.Literal.string(alias.output_name))
    3349            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3350                alias.replace(exp.to_identifier(alias.output_name))
    3351
    3352        return self.alias_sql(expression)

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

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

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

    3357    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3358        this = self.sql(expression, "this")
    3359        index = self.sql(expression, "expression")
    3360        return f"{this} AT {index}"

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

    3362    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3363        this = self.sql(expression, "this")
    3364        zone = self.sql(expression, "zone")
    3365        return f"{this} AT TIME ZONE {zone}"

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

    3367    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3368        this = self.sql(expression, "this")
    3369        zone = self.sql(expression, "zone")
    3370        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

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

    3375    def and_sql(
    3376        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3377    ) -> str:
    3378        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

    3380    def or_sql(
    3381        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3382    ) -> str:
    3383        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

    3385    def xor_sql(
    3386        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3387    ) -> str:
    3388        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

    3390    def connector_sql(
    3391        self,
    3392        expression: exp.Connector,
    3393        op: str,
    3394        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3395    ) -> str:
    3396        if stack is not None:
    3397            if expression.expressions:
    3398                stack.append(self.expressions(expression, sep=f" {op} "))
    3399            else:
    3400                stack.append(expression.right)
    3401                if expression.comments and self.comments:
    3402                    for comment in expression.comments:
    3403                        if comment:
    3404                            op += f" /*{self.sanitize_comment(comment)}*/"
    3405                stack.extend((op, expression.left))
    3406            return op
    3407
    3408        stack = [expression]
    3409        sqls: t.List[str] = []
    3410        ops = set()
    3411
    3412        while stack:
    3413            node = stack.pop()
    3414            if isinstance(node, exp.Connector):
    3415                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3416            else:
    3417                sql = self.sql(node)
    3418                if sqls and sqls[-1] in ops:
    3419                    sqls[-1] += f" {sql}"
    3420                else:
    3421                    sqls.append(sql)
    3422
    3423        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3424        return sep.join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    3444    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3445        format_sql = self.sql(expression, "format")
    3446        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3447        to_sql = self.sql(expression, "to")
    3448        to_sql = f" {to_sql}" if to_sql else ""
    3449        action = self.sql(expression, "action")
    3450        action = f" {action}" if action else ""
    3451        default = self.sql(expression, "default")
    3452        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3453        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

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

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

    3459    def collate_sql(self, expression: exp.Collate) -> str:
    3460        if self.COLLATE_IS_FUNC:
    3461            return self.function_fallback_sql(expression)
    3462        return self.binary(expression, "COLLATE")

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

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

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

    3467    def comment_sql(self, expression: exp.Comment) -> str:
    3468        this = self.sql(expression, "this")
    3469        kind = expression.args["kind"]
    3470        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3471        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3472        expression_sql = self.sql(expression, "expression")
    3473        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"

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

    3475    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3476        this = self.sql(expression, "this")
    3477        delete = " DELETE" if expression.args.get("delete") else ""
    3478        recompress = self.sql(expression, "recompress")
    3479        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3480        to_disk = self.sql(expression, "to_disk")
    3481        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3482        to_volume = self.sql(expression, "to_volume")
    3483        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3484        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    3486    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3487        where = self.sql(expression, "where")
    3488        group = self.sql(expression, "group")
    3489        aggregates = self.expressions(expression, key="aggregates")
    3490        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3491
    3492        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3493            return f"TTL {self.expressions(expression, flat=True)}"
    3494
    3495        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

    3497    def transaction_sql(self, expression: exp.Transaction) -> str:
    3498        modes = self.expressions(expression, key="modes")
    3499        modes = f" {modes}" if modes else ""
    3500        return f"BEGIN{modes}"

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

    3502    def commit_sql(self, expression: exp.Commit) -> str:
    3503        chain = expression.args.get("chain")
    3504        if chain is not None:
    3505            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3506
    3507        return f"COMMIT{chain or ''}"

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

    3509    def rollback_sql(self, expression: exp.Rollback) -> str:
    3510        savepoint = expression.args.get("savepoint")
    3511        savepoint = f" TO {savepoint}" if savepoint else ""
    3512        return f"ROLLBACK{savepoint}"

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

    3514    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3515        this = self.sql(expression, "this")
    3516
    3517        dtype = self.sql(expression, "dtype")
    3518        if dtype:
    3519            collate = self.sql(expression, "collate")
    3520            collate = f" COLLATE {collate}" if collate else ""
    3521            using = self.sql(expression, "using")
    3522            using = f" USING {using}" if using else ""
    3523            alter_set_type = self.ALTER_SET_TYPE + " " if self.ALTER_SET_TYPE else ""
    3524            return f"ALTER COLUMN {this} {alter_set_type}{dtype}{collate}{using}"
    3525
    3526        default = self.sql(expression, "default")
    3527        if default:
    3528            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3529
    3530        comment = self.sql(expression, "comment")
    3531        if comment:
    3532            return f"ALTER COLUMN {this} COMMENT {comment}"
    3533
    3534        visible = expression.args.get("visible")
    3535        if visible:
    3536            return f"ALTER COLUMN {this} SET {visible}"
    3537
    3538        allow_null = expression.args.get("allow_null")
    3539        drop = expression.args.get("drop")
    3540
    3541        if not drop and not allow_null:
    3542            self.unsupported("Unsupported ALTER COLUMN syntax")
    3543
    3544        if allow_null is not None:
    3545            keyword = "DROP" if drop else "SET"
    3546            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3547
    3548        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    3550    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
    3551        this = self.sql(expression, "this")
    3552
    3553        visible = expression.args.get("visible")
    3554        visible_sql = "VISIBLE" if visible else "INVISIBLE"
    3555
    3556        return f"ALTER INDEX {this} {visible_sql}"

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

    3558    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3559        this = self.sql(expression, "this")
    3560        if not isinstance(expression.this, exp.Var):
    3561            this = f"KEY DISTKEY {this}"
    3562        return f"ALTER DISTSTYLE {this}"

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

    3564    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3565        compound = " COMPOUND" if expression.args.get("compound") else ""
    3566        this = self.sql(expression, "this")
    3567        expressions = self.expressions(expression, flat=True)
    3568        expressions = f"({expressions})" if expressions else ""
    3569        return f"ALTER{compound} SORTKEY {this or expressions}"

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

    3571    def alterrename_sql(self, expression: exp.AlterRename, include_to: bool = True) -> str:
    3572        if not self.RENAME_TABLE_WITH_DB:
    3573            # Remove db from tables
    3574            expression = expression.transform(
    3575                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3576            ).assert_is(exp.AlterRename)
    3577        this = self.sql(expression, "this")
    3578        to_kw = " TO" if include_to else ""
    3579        return f"RENAME{to_kw} {this}"

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

    3581    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3582        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3583        old_column = self.sql(expression, "this")
    3584        new_column = self.sql(expression, "to")
    3585        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

    3587    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3588        exprs = self.expressions(expression, flat=True)
    3589        if self.ALTER_SET_WRAPPED:
    3590            exprs = f"({exprs})"
    3591
    3592        return f"SET {exprs}"

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

    3594    def alter_sql(self, expression: exp.Alter) -> str:
    3595        actions = expression.args["actions"]
    3596
    3597        if not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and isinstance(
    3598            actions[0], exp.ColumnDef
    3599        ):
    3600            actions_sql = self.expressions(expression, key="actions", flat=True)
    3601            actions_sql = f"ADD {actions_sql}"
    3602        else:
    3603            actions_list = []
    3604            for action in actions:
    3605                if isinstance(action, (exp.ColumnDef, exp.Schema)):
    3606                    action_sql = self.add_column_sql(action)
    3607                else:
    3608                    action_sql = self.sql(action)
    3609                    if isinstance(action, exp.Query):
    3610                        action_sql = f"AS {action_sql}"
    3611
    3612                actions_list.append(action_sql)
    3613
    3614            actions_sql = self.format_args(*actions_list).lstrip("\n")
    3615
    3616        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3617        on_cluster = self.sql(expression, "cluster")
    3618        on_cluster = f" {on_cluster}" if on_cluster else ""
    3619        only = " ONLY" if expression.args.get("only") else ""
    3620        options = self.expressions(expression, key="options")
    3621        options = f", {options}" if options else ""
    3622        kind = self.sql(expression, "kind")
    3623        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3624        check = " WITH CHECK" if expression.args.get("check") else ""
    3625        this = self.sql(expression, "this")
    3626        this = f" {this}" if this else ""
    3627
    3628        return f"ALTER {kind}{exists}{only}{this}{on_cluster}{check}{self.sep()}{actions_sql}{not_valid}{options}"

def altersession_sql(self, expression: sqlglot.expressions.AlterSession) -> str: View Source

    3630    def altersession_sql(self, expression: exp.AlterSession) -> str:
    3631        items_sql = self.expressions(expression, flat=True)
    3632        keyword = "UNSET" if expression.args.get("unset") else "SET"
    3633        return f"{keyword} {items_sql}"

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

    3635    def add_column_sql(self, expression: exp.Expression) -> str:
    3636        sql = self.sql(expression)
    3637        if isinstance(expression, exp.Schema):
    3638            column_text = " COLUMNS"
    3639        elif isinstance(expression, exp.ColumnDef) and self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3640            column_text = " COLUMN"
    3641        else:
    3642            column_text = ""
    3643
    3644        return f"ADD{column_text} {sql}"

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

    3646    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3647        expressions = self.expressions(expression)
    3648        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3649        return f"DROP{exists}{expressions}"

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

    3651    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3652        return f"ADD {self.expressions(expression, indent=False)}"

def addpartition_sql(self, expression: sqlglot.expressions.AddPartition) -> str: View Source

    3654    def addpartition_sql(self, expression: exp.AddPartition) -> str:
    3655        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    3656        location = self.sql(expression, "location")
    3657        location = f" {location}" if location else ""
    3658        return f"ADD {exists}{self.sql(expression.this)}{location}"

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

    3660    def distinct_sql(self, expression: exp.Distinct) -> str:
    3661        this = self.expressions(expression, flat=True)
    3662
    3663        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3664            case = exp.case()
    3665            for arg in expression.expressions:
    3666                case = case.when(arg.is_(exp.null()), exp.null())
    3667            this = self.sql(case.else_(f"({this})"))
    3668
    3669        this = f" {this}" if this else ""
    3670
    3671        on = self.sql(expression, "on")
    3672        on = f" ON {on}" if on else ""
    3673        return f"DISTINCT{this}{on}"

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

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

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

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

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

    3681    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3682        this_sql = self.sql(expression, "this")
    3683        expression_sql = self.sql(expression, "expression")
    3684        kind = "MAX" if expression.args.get("max") else "MIN"
    3685        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3687    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3688        return self.sql(
    3689            exp.Cast(
    3690                this=exp.Div(this=expression.this, expression=expression.expression),
    3691                to=exp.DataType(this=exp.DataType.Type.INT),
    3692            )
    3693        )

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

    3695    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3696        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3697            return self.func(
    3698                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3699            )
    3700        return self.binary(expression, "||")

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

    3702    def div_sql(self, expression: exp.Div) -> str:
    3703        l, r = expression.left, expression.right
    3704
    3705        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3706            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3707
    3708        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3709            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3710                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3711
    3712        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3713            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3714                return self.sql(
    3715                    exp.cast(
    3716                        l / r,
    3717                        to=exp.DataType.Type.BIGINT,
    3718                    )
    3719                )
    3720
    3721        return self.binary(expression, "/")

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

    3723    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3724        n = exp._wrap(expression.this, exp.Binary)
    3725        d = exp._wrap(expression.expression, exp.Binary)
    3726        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3755    def is_sql(self, expression: exp.Is) -> str:
    3756        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3757            return self.sql(
    3758                expression.this if expression.expression.this else exp.not_(expression.this)
    3759            )
    3760        return self.binary(expression, "IS")

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

    3789    def like_sql(self, expression: exp.Like) -> str:
    3790        return self._like_sql(expression)

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

    3792    def ilike_sql(self, expression: exp.ILike) -> str:
    3793        return self._like_sql(expression)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3831    def try_sql(self, expression: exp.Try) -> str:
    3832        if not self.TRY_SUPPORTED:
    3833            self.unsupported("Unsupported TRY function")
    3834            return self.sql(expression, "this")
    3835
    3836        return self.func("TRY", expression.this)

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

    3838    def log_sql(self, expression: exp.Log) -> str:
    3839        this = expression.this
    3840        expr = expression.expression
    3841
    3842        if self.dialect.LOG_BASE_FIRST is False:
    3843            this, expr = expr, this
    3844        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3845            if this.name in ("2", "10"):
    3846                return self.func(f"LOG{this.name}", expr)
    3847
    3848            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3849
    3850        return self.func("LOG", this, expr)

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

    3852    def use_sql(self, expression: exp.Use) -> str:
    3853        kind = self.sql(expression, "kind")
    3854        kind = f" {kind}" if kind else ""
    3855        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
    3856        this = f" {this}" if this else ""
    3857        return f"USE{kind}{this}"

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

    3859    def binary(self, expression: exp.Binary, op: str) -> str:
    3860        sqls: t.List[str] = []
    3861        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3862        binary_type = type(expression)
    3863
    3864        while stack:
    3865            node = stack.pop()
    3866
    3867            if type(node) is binary_type:
    3868                op_func = node.args.get("operator")
    3869                if op_func:
    3870                    op = f"OPERATOR({self.sql(op_func)})"
    3871
    3872                stack.append(node.right)
    3873                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3874                stack.append(node.left)
    3875            else:
    3876                sqls.append(self.sql(node))
    3877
    3878        return "".join(sqls)

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

    3880    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3881        to_clause = self.sql(expression, "to")
    3882        if to_clause:
    3883            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3884
    3885        return self.function_fallback_sql(expression)

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

    3887    def function_fallback_sql(self, expression: exp.Func) -> str:
    3888        args = []
    3889
    3890        for key in expression.arg_types:
    3891            arg_value = expression.args.get(key)
    3892
    3893            if isinstance(arg_value, list):
    3894                for value in arg_value:
    3895                    args.append(value)
    3896            elif arg_value is not None:
    3897                args.append(arg_value)
    3898
    3899        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3900            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3901        else:
    3902            name = expression.sql_name()
    3903
    3904        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

    3906    def func(
    3907        self,
    3908        name: str,
    3909        *args: t.Optional[exp.Expression | str],
    3910        prefix: str = "(",
    3911        suffix: str = ")",
    3912        normalize: bool = True,
    3913    ) -> str:
    3914        name = self.normalize_func(name) if normalize else name
    3915        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

    3917    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3918        arg_sqls = tuple(
    3919            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3920        )
    3921        if self.pretty and self.too_wide(arg_sqls):
    3922            return self.indent(
    3923                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3924            )
    3925        return sep.join(arg_sqls)

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

    3927    def too_wide(self, args: t.Iterable) -> bool:
    3928        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

    3930    def format_time(
    3931        self,
    3932        expression: exp.Expression,
    3933        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3934        inverse_time_trie: t.Optional[t.Dict] = None,
    3935    ) -> t.Optional[str]:
    3936        return format_time(
    3937            self.sql(expression, "format"),
    3938            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3939            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3940        )

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

    3942    def expressions(
    3943        self,
    3944        expression: t.Optional[exp.Expression] = None,
    3945        key: t.Optional[str] = None,
    3946        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3947        flat: bool = False,
    3948        indent: bool = True,
    3949        skip_first: bool = False,
    3950        skip_last: bool = False,
    3951        sep: str = ", ",
    3952        prefix: str = "",
    3953        dynamic: bool = False,
    3954        new_line: bool = False,
    3955    ) -> str:
    3956        expressions = expression.args.get(key or "expressions") if expression else sqls
    3957
    3958        if not expressions:
    3959            return ""
    3960
    3961        if flat:
    3962            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3963
    3964        num_sqls = len(expressions)
    3965        result_sqls = []
    3966
    3967        for i, e in enumerate(expressions):
    3968            sql = self.sql(e, comment=False)
    3969            if not sql:
    3970                continue
    3971
    3972            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3973
    3974            if self.pretty:
    3975                if self.leading_comma:
    3976                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3977                else:
    3978                    result_sqls.append(
    3979                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3980                    )
    3981            else:
    3982                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3983
    3984        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3985            if new_line:
    3986                result_sqls.insert(0, "")
    3987                result_sqls.append("")
    3988            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3989        else:
    3990            result_sql = "".join(result_sqls)
    3991
    3992        return (
    3993            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3994            if indent
    3995            else result_sql
    3996        )

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

    3998    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3999        flat = flat or isinstance(expression.parent, exp.Properties)
    4000        expressions_sql = self.expressions(expression, flat=flat)
    4001        if flat:
    4002            return f"{op} {expressions_sql}"
    4003        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    4005    def naked_property(self, expression: exp.Property) -> str:
    4006        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    4007        if not property_name:
    4008            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    4009        return f"{property_name} {self.sql(expression, 'this')}"

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

    4011    def tag_sql(self, expression: exp.Tag) -> str:
    4012        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

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

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

    4017    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    4018        this = self.sql(expression, "this")
    4019        expressions = self.no_identify(self.expressions, expression)
    4020        expressions = (
    4021            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    4022        )
    4023        return f"{this}{expressions}" if expressions.strip() != "" else this

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

    4025    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    4026        this = self.sql(expression, "this")
    4027        expressions = self.expressions(expression, flat=True)
    4028        return f"{this}({expressions})"

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

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

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

    4033    def when_sql(self, expression: exp.When) -> str:
    4034        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    4035        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    4036        condition = self.sql(expression, "condition")
    4037        condition = f" AND {condition}" if condition else ""
    4038
    4039        then_expression = expression.args.get("then")
    4040        if isinstance(then_expression, exp.Insert):
    4041            this = self.sql(then_expression, "this")
    4042            this = f"INSERT {this}" if this else "INSERT"
    4043            then = self.sql(then_expression, "expression")
    4044            then = f"{this} VALUES {then}" if then else this
    4045        elif isinstance(then_expression, exp.Update):
    4046            if isinstance(then_expression.args.get("expressions"), exp.Star):
    4047                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    4048            else:
    4049                then = f"UPDATE SET{self.sep()}{self.expressions(then_expression)}"
    4050        else:
    4051            then = self.sql(then_expression)
    4052        return f"WHEN {matched}{source}{condition} THEN {then}"

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

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

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

    4057    def merge_sql(self, expression: exp.Merge) -> str:
    4058        table = expression.this
    4059        table_alias = ""
    4060
    4061        hints = table.args.get("hints")
    4062        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    4063            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    4064            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    4065
    4066        this = self.sql(table)
    4067        using = f"USING {self.sql(expression, 'using')}"
    4068        on = f"ON {self.sql(expression, 'on')}"
    4069        whens = self.sql(expression, "whens")
    4070
    4071        returning = self.sql(expression, "returning")
    4072        if returning:
    4073            whens = f"{whens}{returning}"
    4074
    4075        sep = self.sep()
    4076
    4077        return self.prepend_ctes(
    4078            expression,
    4079            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    4080        )

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

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

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

    4086    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    4087        if not self.SUPPORTS_TO_NUMBER:
    4088            self.unsupported("Unsupported TO_NUMBER function")
    4089            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    4090
    4091        fmt = expression.args.get("format")
    4092        if not fmt:
    4093            self.unsupported("Conversion format is required for TO_NUMBER")
    4094            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    4095
    4096        return self.func("TO_NUMBER", expression.this, fmt)

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

    4098    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    4099        this = self.sql(expression, "this")
    4100        kind = self.sql(expression, "kind")
    4101        settings_sql = self.expressions(expression, key="settings", sep=" ")
    4102        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    4103        return f"{this}({kind}{args})"

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

    4105    def dictrange_sql(self, expression: exp.DictRange) -> str:
    4106        this = self.sql(expression, "this")
    4107        max = self.sql(expression, "max")
    4108        min = self.sql(expression, "min")
    4109        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

def uniquekeyproperty_sql( self, expression: sqlglot.expressions.UniqueKeyProperty, prefix: str = 'UNIQUE KEY') -> str: View Source

    4118    def uniquekeyproperty_sql(
    4119        self, expression: exp.UniqueKeyProperty, prefix: str = "UNIQUE KEY"
    4120    ) -> str:
    4121        return f"{prefix} ({self.expressions(expression, flat=True)})"

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

    4124    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    4125        expressions = self.expressions(expression, flat=True)
    4126        expressions = f" {self.wrap(expressions)}" if expressions else ""
    4127        buckets = self.sql(expression, "buckets")
    4128        kind = self.sql(expression, "kind")
    4129        buckets = f" BUCKETS {buckets}" if buckets else ""
    4130        order = self.sql(expression, "order")
    4131        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"

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

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

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

    4136    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    4137        expressions = self.expressions(expression, key="expressions", flat=True)
    4138        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    4139        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    4140        buckets = self.sql(expression, "buckets")
    4141        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    4143    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    4144        this = self.sql(expression, "this")
    4145        having = self.sql(expression, "having")
    4146
    4147        if having:
    4148            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    4149
    4150        return self.func("ANY_VALUE", this)

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

    4152    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    4153        transform = self.func("TRANSFORM", *expression.expressions)
    4154        row_format_before = self.sql(expression, "row_format_before")
    4155        row_format_before = f" {row_format_before}" if row_format_before else ""
    4156        record_writer = self.sql(expression, "record_writer")
    4157        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    4158        using = f" USING {self.sql(expression, 'command_script')}"
    4159        schema = self.sql(expression, "schema")
    4160        schema = f" AS {schema}" if schema else ""
    4161        row_format_after = self.sql(expression, "row_format_after")
    4162        row_format_after = f" {row_format_after}" if row_format_after else ""
    4163        record_reader = self.sql(expression, "record_reader")
    4164        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    4165        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

    4167    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    4168        key_block_size = self.sql(expression, "key_block_size")
    4169        if key_block_size:
    4170            return f"KEY_BLOCK_SIZE = {key_block_size}"
    4171
    4172        using = self.sql(expression, "using")
    4173        if using:
    4174            return f"USING {using}"
    4175
    4176        parser = self.sql(expression, "parser")
    4177        if parser:
    4178            return f"WITH PARSER {parser}"
    4179
    4180        comment = self.sql(expression, "comment")
    4181        if comment:
    4182            return f"COMMENT {comment}"
    4183
    4184        visible = expression.args.get("visible")
    4185        if visible is not None:
    4186            return "VISIBLE" if visible else "INVISIBLE"
    4187
    4188        engine_attr = self.sql(expression, "engine_attr")
    4189        if engine_attr:
    4190            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    4191
    4192        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    4193        if secondary_engine_attr:
    4194            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    4195
    4196        self.unsupported("Unsupported index constraint option.")
    4197        return ""

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

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

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

    4203    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    4204        kind = self.sql(expression, "kind")
    4205        kind = f"{kind} INDEX" if kind else "INDEX"
    4206        this = self.sql(expression, "this")
    4207        this = f" {this}" if this else ""
    4208        index_type = self.sql(expression, "index_type")
    4209        index_type = f" USING {index_type}" if index_type else ""
    4210        expressions = self.expressions(expression, flat=True)
    4211        expressions = f" ({expressions})" if expressions else ""
    4212        options = self.expressions(expression, key="options", sep=" ")
    4213        options = f" {options}" if options else ""
    4214        return f"{kind}{this}{index_type}{expressions}{options}"

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

    4216    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    4217        if self.NVL2_SUPPORTED:
    4218            return self.function_fallback_sql(expression)
    4219
    4220        case = exp.Case().when(
    4221            expression.this.is_(exp.null()).not_(copy=False),
    4222            expression.args["true"],
    4223            copy=False,
    4224        )
    4225        else_cond = expression.args.get("false")
    4226        if else_cond:
    4227            case.else_(else_cond, copy=False)
    4228
    4229        return self.sql(case)

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

    4231    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    4232        this = self.sql(expression, "this")
    4233        expr = self.sql(expression, "expression")
    4234        iterator = self.sql(expression, "iterator")
    4235        condition = self.sql(expression, "condition")
    4236        condition = f" IF {condition}" if condition else ""
    4237        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    4259    def predict_sql(self, expression: exp.Predict) -> str:
    4260        return self._ml_sql(expression, "PREDICT")

def generateembedding_sql(self, expression: sqlglot.expressions.GenerateEmbedding) -> str: View Source

    4262    def generateembedding_sql(self, expression: exp.GenerateEmbedding) -> str:
    4263        name = "GENERATE_TEXT_EMBEDDING" if expression.args.get("is_text") else "GENERATE_EMBEDDING"
    4264        return self._ml_sql(expression, name)

def mltranslate_sql(self, expression: sqlglot.expressions.MLTranslate) -> str: View Source

    4266    def mltranslate_sql(self, expression: exp.MLTranslate) -> str:
    4267        return self._ml_sql(expression, "TRANSLATE")

def mlforecast_sql(self, expression: sqlglot.expressions.MLForecast) -> str: View Source

    4269    def mlforecast_sql(self, expression: exp.MLForecast) -> str:
    4270        return self._ml_sql(expression, "FORECAST")

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

    4272    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4273        this_sql = self.sql(expression, "this")
    4274        if isinstance(expression.this, exp.Table):
    4275            this_sql = f"TABLE {this_sql}"
    4276
    4277        return self.func(
    4278            "FEATURES_AT_TIME",
    4279            this_sql,
    4280            expression.args.get("time"),
    4281            expression.args.get("num_rows"),
    4282            expression.args.get("ignore_feature_nulls"),
    4283        )

def vectorsearch_sql(self, expression: sqlglot.expressions.VectorSearch) -> str: View Source

    4285    def vectorsearch_sql(self, expression: exp.VectorSearch) -> str:
    4286        this_sql = self.sql(expression, "this")
    4287        if isinstance(expression.this, exp.Table):
    4288            this_sql = f"TABLE {this_sql}"
    4289
    4290        query_table = self.sql(expression, "query_table")
    4291        if isinstance(expression.args["query_table"], exp.Table):
    4292            query_table = f"TABLE {query_table}"
    4293
    4294        return self.func(
    4295            "VECTOR_SEARCH",
    4296            this_sql,
    4297            expression.args.get("column_to_search"),
    4298            query_table,
    4299            expression.args.get("query_column_to_search"),
    4300            expression.args.get("top_k"),
    4301            expression.args.get("distance_type"),
    4302            expression.args.get("options"),
    4303        )

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

    4305    def forin_sql(self, expression: exp.ForIn) -> str:
    4306        this = self.sql(expression, "this")
    4307        expression_sql = self.sql(expression, "expression")
    4308        return f"FOR {this} DO {expression_sql}"

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

    4310    def refresh_sql(self, expression: exp.Refresh) -> str:
    4311        this = self.sql(expression, "this")
    4312        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    4313        return f"REFRESH {table}{this}"

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

    4315    def toarray_sql(self, expression: exp.ToArray) -> str:
    4316        arg = expression.this
    4317        if not arg.type:
    4318            from sqlglot.optimizer.annotate_types import annotate_types
    4319
    4320            arg = annotate_types(arg, dialect=self.dialect)
    4321
    4322        if arg.is_type(exp.DataType.Type.ARRAY):
    4323            return self.sql(arg)
    4324
    4325        cond_for_null = arg.is_(exp.null())
    4326        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

    4328    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    4329        this = expression.this
    4330        time_format = self.format_time(expression)
    4331
    4332        if time_format:
    4333            return self.sql(
    4334                exp.cast(
    4335                    exp.StrToTime(this=this, format=expression.args["format"]),
    4336                    exp.DataType.Type.TIME,
    4337                )
    4338            )
    4339
    4340        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    4341            return self.sql(this)
    4342
    4343        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

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

    4345    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    4346        this = expression.this
    4347        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    4348            return self.sql(this)
    4349
    4350        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))

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

    4352    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    4353        this = expression.this
    4354        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    4355            return self.sql(this)
    4356
    4357        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))

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

    4359    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    4360        this = expression.this
    4361        time_format = self.format_time(expression)
    4362
    4363        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    4364            return self.sql(
    4365                exp.cast(
    4366                    exp.StrToTime(this=this, format=expression.args["format"]),
    4367                    exp.DataType.Type.DATE,
    4368                )
    4369            )
    4370
    4371        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    4372            return self.sql(this)
    4373
    4374        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

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

    4376    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    4377        return self.sql(
    4378            exp.func(
    4379                "DATEDIFF",
    4380                expression.this,
    4381                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    4382                "day",
    4383            )
    4384        )

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

    4386    def lastday_sql(self, expression: exp.LastDay) -> str:
    4387        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4388            return self.function_fallback_sql(expression)
    4389
    4390        unit = expression.text("unit")
    4391        if unit and unit != "MONTH":
    4392            self.unsupported("Date parts are not supported in LAST_DAY.")
    4393
    4394        return self.func("LAST_DAY", expression.this)

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

    4396    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4397        from sqlglot.dialects.dialect import unit_to_str
    4398
    4399        return self.func(
    4400            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4401        )

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

    4403    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4404        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4405            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4406            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4407            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4408            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4409
    4410        from sqlglot.dialects import Dialect
    4411
    4412        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4413        if self.dialect.__class__ != Dialect:
    4414            self.unsupported("ARRAY_ANY is unsupported")
    4415
    4416        return self.function_fallback_sql(expression)

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

    4418    def struct_sql(self, expression: exp.Struct) -> str:
    4419        expression.set(
    4420            "expressions",
    4421            [
    4422                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4423                if isinstance(e, exp.PropertyEQ)
    4424                else e
    4425                for e in expression.expressions
    4426            ],
    4427        )
    4428
    4429        return self.function_fallback_sql(expression)

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

    4431    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4432        low = self.sql(expression, "this")
    4433        high = self.sql(expression, "expression")
    4434
    4435        return f"{low} TO {high}"

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

    4437    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4438        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4439        tables = f" {self.expressions(expression)}"
    4440
    4441        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4442
    4443        on_cluster = self.sql(expression, "cluster")
    4444        on_cluster = f" {on_cluster}" if on_cluster else ""
    4445
    4446        identity = self.sql(expression, "identity")
    4447        identity = f" {identity} IDENTITY" if identity else ""
    4448
    4449        option = self.sql(expression, "option")
    4450        option = f" {option}" if option else ""
    4451
    4452        partition = self.sql(expression, "partition")
    4453        partition = f" {partition}" if partition else ""
    4454
    4455        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

    4459    def convert_sql(self, expression: exp.Convert) -> str:
    4460        to = expression.this
    4461        value = expression.expression
    4462        style = expression.args.get("style")
    4463        safe = expression.args.get("safe")
    4464        strict = expression.args.get("strict")
    4465
    4466        if not to or not value:
    4467            return ""
    4468
    4469        # Retrieve length of datatype and override to default if not specified
    4470        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4471            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4472
    4473        transformed: t.Optional[exp.Expression] = None
    4474        cast = exp.Cast if strict else exp.TryCast
    4475
    4476        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4477        if isinstance(style, exp.Literal) and style.is_int:
    4478            from sqlglot.dialects.tsql import TSQL
    4479
    4480            style_value = style.name
    4481            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4482            if not converted_style:
    4483                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4484
    4485            fmt = exp.Literal.string(converted_style)
    4486
    4487            if to.this == exp.DataType.Type.DATE:
    4488                transformed = exp.StrToDate(this=value, format=fmt)
    4489            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4490                transformed = exp.StrToTime(this=value, format=fmt)
    4491            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4492                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4493            elif to.this == exp.DataType.Type.TEXT:
    4494                transformed = exp.TimeToStr(this=value, format=fmt)
    4495
    4496        if not transformed:
    4497            transformed = cast(this=value, to=to, safe=safe)
    4498
    4499        return self.sql(transformed)

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

    4567    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4568        option = self.sql(expression, "this")
    4569
    4570        if expression.expressions:
    4571            upper = option.upper()
    4572
    4573            # Snowflake FILE_FORMAT options are separated by whitespace
    4574            sep = " " if upper == "FILE_FORMAT" else ", "
    4575
    4576            # Databricks copy/format options do not set their list of values with EQ
    4577            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4578            values = self.expressions(expression, flat=True, sep=sep)
    4579            return f"{option}{op}({values})"
    4580
    4581        value = self.sql(expression, "expression")
    4582
    4583        if not value:
    4584            return option
    4585
    4586        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4587
    4588        return f"{option}{op}{value}"

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

    4590    def credentials_sql(self, expression: exp.Credentials) -> str:
    4591        cred_expr = expression.args.get("credentials")
    4592        if isinstance(cred_expr, exp.Literal):
    4593            # Redshift case: CREDENTIALS <string>
    4594            credentials = self.sql(expression, "credentials")
    4595            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4596        else:
    4597            # Snowflake case: CREDENTIALS = (...)
    4598            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4599            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4600
    4601        storage = self.sql(expression, "storage")
    4602        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4603
    4604        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4605        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4606
    4607        iam_role = self.sql(expression, "iam_role")
    4608        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4609
    4610        region = self.sql(expression, "region")
    4611        region = f" REGION {region}" if region else ""
    4612
    4613        return f"{credentials}{storage}{encryption}{iam_role}{region}"

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

    4615    def copy_sql(self, expression: exp.Copy) -> str:
    4616        this = self.sql(expression, "this")
    4617        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4618
    4619        credentials = self.sql(expression, "credentials")
    4620        credentials = self.seg(credentials) if credentials else ""
    4621        files = self.expressions(expression, key="files", flat=True)
    4622        kind = self.seg("FROM" if expression.args.get("kind") else "TO") if files else ""
    4623
    4624        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4625        params = self.expressions(
    4626            expression,
    4627            key="params",
    4628            sep=sep,
    4629            new_line=True,
    4630            skip_last=True,
    4631            skip_first=True,
    4632            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4633        )
    4634
    4635        if params:
    4636            if self.COPY_PARAMS_ARE_WRAPPED:
    4637                params = f" WITH ({params})"
    4638            elif not self.pretty and (files or credentials):
    4639                params = f" {params}"
    4640
    4641        return f"COPY{this}{kind} {files}{credentials}{params}"

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

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

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

    4646    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4647        on_sql = "ON" if expression.args.get("on") else "OFF"
    4648        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4649        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4650        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4651        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4652
    4653        if filter_col or retention_period:
    4654            on_sql = self.func("ON", filter_col, retention_period)
    4655
    4656        return f"DATA_DELETION={on_sql}"

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

    4658    def maskingpolicycolumnconstraint_sql(
    4659        self, expression: exp.MaskingPolicyColumnConstraint
    4660    ) -> str:
    4661        this = self.sql(expression, "this")
    4662        expressions = self.expressions(expression, flat=True)
    4663        expressions = f" USING ({expressions})" if expressions else ""
    4664        return f"MASKING POLICY {this}{expressions}"

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

    4666    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4667        this = self.sql(expression, "this")
    4668        this = f"TABLE {this}"
    4669        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

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

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

    4674    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4675        this = self.sql(expression, "this")
    4676        expr = expression.expression
    4677
    4678        if isinstance(expr, exp.Func):
    4679            # T-SQL's CLR functions are case sensitive
    4680            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4681        else:
    4682            expr = self.sql(expression, "expression")
    4683
    4684        return self.scope_resolution(expr, this)

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

    4686    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4687        if self.PARSE_JSON_NAME is None:
    4688            return self.sql(expression.this)
    4689
    4690        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)

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

    4692    def rand_sql(self, expression: exp.Rand) -> str:
    4693        lower = self.sql(expression, "lower")
    4694        upper = self.sql(expression, "upper")
    4695
    4696        if lower and upper:
    4697            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4698        return self.func("RAND", expression.this)

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

    4700    def changes_sql(self, expression: exp.Changes) -> str:
    4701        information = self.sql(expression, "information")
    4702        information = f"INFORMATION => {information}"
    4703        at_before = self.sql(expression, "at_before")
    4704        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4705        end = self.sql(expression, "end")
    4706        end = f"{self.seg('')}{end}" if end else ""
    4707
    4708        return f"CHANGES ({information}){at_before}{end}"

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

    4710    def pad_sql(self, expression: exp.Pad) -> str:
    4711        prefix = "L" if expression.args.get("is_left") else "R"
    4712
    4713        fill_pattern = self.sql(expression, "fill_pattern") or None
    4714        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4715            fill_pattern = "' '"
    4716
    4717        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)

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

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

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

    4723    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4724        generate_series = exp.GenerateSeries(**expression.args)
    4725
    4726        parent = expression.parent
    4727        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4728            parent = parent.parent
    4729
    4730        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4731            return self.sql(exp.Unnest(expressions=[generate_series]))
    4732
    4733        if isinstance(parent, exp.Select):
    4734            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4735
    4736        return self.sql(generate_series)

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

    4738    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4739        exprs = expression.expressions
    4740        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4741            if len(exprs) == 0:
    4742                rhs: t.Union[str, exp.Expression] = exp.Array(expressions=[])
    4743            else:
    4744                rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4745        else:
    4746            rhs = self.expressions(expression)  # type: ignore
    4747
    4748        return self.func(name, expression.this, rhs or None)

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

    4750    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4751        if self.SUPPORTS_CONVERT_TIMEZONE:
    4752            return self.function_fallback_sql(expression)
    4753
    4754        source_tz = expression.args.get("source_tz")
    4755        target_tz = expression.args.get("target_tz")
    4756        timestamp = expression.args.get("timestamp")
    4757
    4758        if source_tz and timestamp:
    4759            timestamp = exp.AtTimeZone(
    4760                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4761            )
    4762
    4763        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4764
    4765        return self.sql(expr)

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

    4767    def json_sql(self, expression: exp.JSON) -> str:
    4768        this = self.sql(expression, "this")
    4769        this = f" {this}" if this else ""
    4770
    4771        _with = expression.args.get("with")
    4772
    4773        if _with is None:
    4774            with_sql = ""
    4775        elif not _with:
    4776            with_sql = " WITHOUT"
    4777        else:
    4778            with_sql = " WITH"
    4779
    4780        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4781
    4782        return f"JSON{this}{with_sql}{unique_sql}"

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

    4784    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4785        def _generate_on_options(arg: t.Any) -> str:
    4786            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4787
    4788        path = self.sql(expression, "path")
    4789        returning = self.sql(expression, "returning")
    4790        returning = f" RETURNING {returning}" if returning else ""
    4791
    4792        on_condition = self.sql(expression, "on_condition")
    4793        on_condition = f" {on_condition}" if on_condition else ""
    4794
    4795        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")

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

    4797    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4798        else_ = "ELSE " if expression.args.get("else_") else ""
    4799        condition = self.sql(expression, "expression")
    4800        condition = f"WHEN {condition} THEN " if condition else else_
    4801        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4802        return f"{condition}{insert}"

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

    4804    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4805        kind = self.sql(expression, "kind")
    4806        expressions = self.seg(self.expressions(expression, sep=" "))
    4807        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4808        return res

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

    4810    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4811        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4812        empty = expression.args.get("empty")
    4813        empty = (
    4814            f"DEFAULT {empty} ON EMPTY"
    4815            if isinstance(empty, exp.Expression)
    4816            else self.sql(expression, "empty")
    4817        )
    4818
    4819        error = expression.args.get("error")
    4820        error = (
    4821            f"DEFAULT {error} ON ERROR"
    4822            if isinstance(error, exp.Expression)
    4823            else self.sql(expression, "error")
    4824        )
    4825
    4826        if error and empty:
    4827            error = (
    4828                f"{empty} {error}"
    4829                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4830                else f"{error} {empty}"
    4831            )
    4832            empty = ""
    4833
    4834        null = self.sql(expression, "null")
    4835
    4836        return f"{empty}{error}{null}"

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

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

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

    4842    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4843        this = self.sql(expression, "this")
    4844        path = self.sql(expression, "path")
    4845
    4846        passing = self.expressions(expression, "passing")
    4847        passing = f" PASSING {passing}" if passing else ""
    4848
    4849        on_condition = self.sql(expression, "on_condition")
    4850        on_condition = f" {on_condition}" if on_condition else ""
    4851
    4852        path = f"{path}{passing}{on_condition}"
    4853
    4854        return self.func("JSON_EXISTS", this, path)

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

    4856    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4857        array_agg = self.function_fallback_sql(expression)
    4858
    4859        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4860        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4861        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4862            parent = expression.parent
    4863            if isinstance(parent, exp.Filter):
    4864                parent_cond = parent.expression.this
    4865                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4866            else:
    4867                this = expression.this
    4868                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4869                if this.find(exp.Column):
    4870                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4871                    this_sql = (
    4872                        self.expressions(this)
    4873                        if isinstance(this, exp.Distinct)
    4874                        else self.sql(expression, "this")
    4875                    )
    4876
    4877                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4878
    4879        return array_agg

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

    4881    def apply_sql(self, expression: exp.Apply) -> str:
    4882        this = self.sql(expression, "this")
    4883        expr = self.sql(expression, "expression")
    4884
    4885        return f"{this} APPLY({expr})"

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

    4914    def grant_sql(self, expression: exp.Grant) -> str:
    4915        return self._grant_or_revoke_sql(
    4916            expression,
    4917            keyword="GRANT",
    4918            preposition="TO",
    4919            grant_option_suffix=" WITH GRANT OPTION",
    4920        )

def revoke_sql(self, expression: sqlglot.expressions.Revoke) -> str: View Source

    4922    def revoke_sql(self, expression: exp.Revoke) -> str:
    4923        return self._grant_or_revoke_sql(
    4924            expression,
    4925            keyword="REVOKE",
    4926            preposition="FROM",
    4927            grant_option_prefix="GRANT OPTION FOR ",
    4928        )

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

    4930    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4931        this = self.sql(expression, "this")
    4932        columns = self.expressions(expression, flat=True)
    4933        columns = f"({columns})" if columns else ""
    4934
    4935        return f"{this}{columns}"

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

    4937    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4938        this = self.sql(expression, "this")
    4939
    4940        kind = self.sql(expression, "kind")
    4941        kind = f"{kind} " if kind else ""
    4942
    4943        return f"{kind}{this}"

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

    4945    def columns_sql(self, expression: exp.Columns):
    4946        func = self.function_fallback_sql(expression)
    4947        if expression.args.get("unpack"):
    4948            func = f"*{func}"
    4949
    4950        return func

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

    4952    def overlay_sql(self, expression: exp.Overlay):
    4953        this = self.sql(expression, "this")
    4954        expr = self.sql(expression, "expression")
    4955        from_sql = self.sql(expression, "from")
    4956        for_sql = self.sql(expression, "for")
    4957        for_sql = f" FOR {for_sql}" if for_sql else ""
    4958
    4959        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

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

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

    4965    def string_sql(self, expression: exp.String) -> str:
    4966        this = expression.this
    4967        zone = expression.args.get("zone")
    4968
    4969        if zone:
    4970            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4971            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4972            # set for source_tz to transpile the time conversion before the STRING cast
    4973            this = exp.ConvertTimezone(
    4974                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4975            )
    4976
    4977        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))

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

    4979    def median_sql(self, expression: exp.Median):
    4980        if not self.SUPPORTS_MEDIAN:
    4981            return self.sql(
    4982                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4983            )
    4984
    4985        return self.function_fallback_sql(expression)

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

    4987    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4988        filler = self.sql(expression, "this")
    4989        filler = f" {filler}" if filler else ""
    4990        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4991        return f"TRUNCATE{filler} {with_count}"

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

    4993    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4994        if self.SUPPORTS_UNIX_SECONDS:
    4995            return self.function_fallback_sql(expression)
    4996
    4997        start_ts = exp.cast(
    4998            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4999        )
    5000
    5001        return self.sql(
    5002            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    5003        )

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

    5005    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    5006        dim = expression.expression
    5007
    5008        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    5009        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    5010            if not (dim.is_int and dim.name == "1"):
    5011                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    5012            dim = None
    5013
    5014        # If dimension is required but not specified, default initialize it
    5015        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    5016            dim = exp.Literal.number(1)
    5017
    5018        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)

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

    5020    def attach_sql(self, expression: exp.Attach) -> str:
    5021        this = self.sql(expression, "this")
    5022        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    5023        expressions = self.expressions(expression)
    5024        expressions = f" ({expressions})" if expressions else ""
    5025
    5026        return f"ATTACH{exists_sql} {this}{expressions}"

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

    5028    def detach_sql(self, expression: exp.Detach) -> str:
    5029        this = self.sql(expression, "this")
    5030        # the DATABASE keyword is required if IF EXISTS is set
    5031        # without it, DuckDB throws an error: Parser Error: syntax error at or near "exists" (Line Number: 1)
    5032        # ref: https://duckdb.org/docs/stable/sql/statements/attach.html#detach-syntax
    5033        exists_sql = " DATABASE IF EXISTS" if expression.args.get("exists") else ""
    5034
    5035        return f"DETACH{exists_sql} {this}"

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

    5037    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    5038        this = self.sql(expression, "this")
    5039        value = self.sql(expression, "expression")
    5040        value = f" {value}" if value else ""
    5041        return f"{this}{value}"

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

    5043    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    5044        return (
    5045            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    5046        )

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

    5048    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    5049        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    5050        encode = f"{encode} {self.sql(expression, 'this')}"
    5051
    5052        properties = expression.args.get("properties")
    5053        if properties:
    5054            encode = f"{encode} {self.properties(properties)}"
    5055
    5056        return encode

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

    5058    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    5059        this = self.sql(expression, "this")
    5060        include = f"INCLUDE {this}"
    5061
    5062        column_def = self.sql(expression, "column_def")
    5063        if column_def:
    5064            include = f"{include} {column_def}"
    5065
    5066        alias = self.sql(expression, "alias")
    5067        if alias:
    5068            include = f"{include} AS {alias}"
    5069
    5070        return include

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

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

def xmlkeyvalueoption_sql(self, expression: sqlglot.expressions.XMLKeyValueOption) -> str: View Source

    5076    def xmlkeyvalueoption_sql(self, expression: exp.XMLKeyValueOption) -> str:
    5077        this = self.sql(expression, "this")
    5078        expr = self.sql(expression, "expression")
    5079        expr = f"({expr})" if expr else ""
    5080        return f"{this}{expr}"

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

    5082    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    5083        partitions = self.expressions(expression, "partition_expressions")
    5084        create = self.expressions(expression, "create_expressions")
    5085        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"

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

    5087    def partitionbyrangepropertydynamic_sql(
    5088        self, expression: exp.PartitionByRangePropertyDynamic
    5089    ) -> str:
    5090        start = self.sql(expression, "start")
    5091        end = self.sql(expression, "end")
    5092
    5093        every = expression.args["every"]
    5094        if isinstance(every, exp.Interval) and every.this.is_string:
    5095            every.this.replace(exp.Literal.number(every.name))
    5096
    5097        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

    5099    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    5100        name = self.sql(expression, "this")
    5101        values = self.expressions(expression, flat=True)
    5102
    5103        return f"NAME {name} VALUE {values}"

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

    5105    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    5106        kind = self.sql(expression, "kind")
    5107        sample = self.sql(expression, "sample")
    5108        return f"SAMPLE {sample} {kind}"

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

    5110    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    5111        kind = self.sql(expression, "kind")
    5112        option = self.sql(expression, "option")
    5113        option = f" {option}" if option else ""
    5114        this = self.sql(expression, "this")
    5115        this = f" {this}" if this else ""
    5116        columns = self.expressions(expression)
    5117        columns = f" {columns}" if columns else ""
    5118        return f"{kind}{option} STATISTICS{this}{columns}"

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

    5120    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    5121        this = self.sql(expression, "this")
    5122        columns = self.expressions(expression)
    5123        inner_expression = self.sql(expression, "expression")
    5124        inner_expression = f" {inner_expression}" if inner_expression else ""
    5125        update_options = self.sql(expression, "update_options")
    5126        update_options = f" {update_options} UPDATE" if update_options else ""
    5127        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"

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

    5129    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    5130        kind = self.sql(expression, "kind")
    5131        kind = f" {kind}" if kind else ""
    5132        return f"DELETE{kind} STATISTICS"

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

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

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

    5138    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    5139        kind = self.sql(expression, "kind")
    5140        this = self.sql(expression, "this")
    5141        this = f" {this}" if this else ""
    5142        inner_expression = self.sql(expression, "expression")
    5143        return f"VALIDATE {kind}{this}{inner_expression}"

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

    5145    def analyze_sql(self, expression: exp.Analyze) -> str:
    5146        options = self.expressions(expression, key="options", sep=" ")
    5147        options = f" {options}" if options else ""
    5148        kind = self.sql(expression, "kind")
    5149        kind = f" {kind}" if kind else ""
    5150        this = self.sql(expression, "this")
    5151        this = f" {this}" if this else ""
    5152        mode = self.sql(expression, "mode")
    5153        mode = f" {mode}" if mode else ""
    5154        properties = self.sql(expression, "properties")
    5155        properties = f" {properties}" if properties else ""
    5156        partition = self.sql(expression, "partition")
    5157        partition = f" {partition}" if partition else ""
    5158        inner_expression = self.sql(expression, "expression")
    5159        inner_expression = f" {inner_expression}" if inner_expression else ""
    5160        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"

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

    5162    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    5163        this = self.sql(expression, "this")
    5164        namespaces = self.expressions(expression, key="namespaces")
    5165        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    5166        passing = self.expressions(expression, key="passing")
    5167        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    5168        columns = self.expressions(expression, key="columns")
    5169        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    5170        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    5171        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

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

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

    5177    def export_sql(self, expression: exp.Export) -> str:
    5178        this = self.sql(expression, "this")
    5179        connection = self.sql(expression, "connection")
    5180        connection = f"WITH CONNECTION {connection} " if connection else ""
    5181        options = self.sql(expression, "options")
    5182        return f"EXPORT DATA {connection}{options} AS {this}"

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

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

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

    5187    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
    5188        variable = self.sql(expression, "this")
    5189        default = self.sql(expression, "default")
    5190        default = f" = {default}" if default else ""
    5191
    5192        kind = self.sql(expression, "kind")
    5193        if isinstance(expression.args.get("kind"), exp.Schema):
    5194            kind = f"TABLE {kind}"
    5195
    5196        return f"{variable} AS {kind}{default}"

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

    5198    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
    5199        kind = self.sql(expression, "kind")
    5200        this = self.sql(expression, "this")
    5201        set = self.sql(expression, "expression")
    5202        using = self.sql(expression, "using")
    5203        using = f" USING {using}" if using else ""
    5204
    5205        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
    5206
    5207        return f"{kind_sql} {this} SET {set}{using}"

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

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

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

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

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

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

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

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

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

    5222    def show_sql(self, expression: exp.Show) -> str:
    5223        self.unsupported("Unsupported SHOW statement")
    5224        return ""

def install_sql(self, expression: sqlglot.expressions.Install) -> str: View Source

    5226    def install_sql(self, expression: exp.Install) -> str:
    5227        self.unsupported("Unsupported INSTALL statement")
    5228        return ""

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

    5230    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
    5231        # Snowflake GET/PUT statements:
    5232        #   PUT <file> <internalStage> <properties>
    5233        #   GET <internalStage> <file> <properties>
    5234        props = expression.args.get("properties")
    5235        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
    5236        this = self.sql(expression, "this")
    5237        target = self.sql(expression, "target")
    5238
    5239        if isinstance(expression, exp.Put):
    5240            return f"PUT {this} {target}{props_sql}"
    5241        else:
    5242            return f"GET {target} {this}{props_sql}"

def translatecharacters_sql(self, expression: sqlglot.expressions.TranslateCharacters): View Source

    5244    def translatecharacters_sql(self, expression: exp.TranslateCharacters):
    5245        this = self.sql(expression, "this")
    5246        expr = self.sql(expression, "expression")
    5247        with_error = " WITH ERROR" if expression.args.get("with_error") else ""
    5248        return f"TRANSLATE({this} USING {expr}{with_error})"

def decodecase_sql(self, expression: sqlglot.expressions.DecodeCase) -> str: View Source

    5250    def decodecase_sql(self, expression: exp.DecodeCase) -> str:
    5251        if self.SUPPORTS_DECODE_CASE:
    5252            return self.func("DECODE", *expression.expressions)
    5253
    5254        expression, *expressions = expression.expressions
    5255
    5256        ifs = []
    5257        for search, result in zip(expressions[::2], expressions[1::2]):
    5258            if isinstance(search, exp.Literal):
    5259                ifs.append(exp.If(this=expression.eq(search), true=result))
    5260            elif isinstance(search, exp.Null):
    5261                ifs.append(exp.If(this=expression.is_(exp.Null()), true=result))
    5262            else:
    5263                if isinstance(search, exp.Binary):
    5264                    search = exp.paren(search)
    5265
    5266                cond = exp.or_(
    5267                    expression.eq(search),
    5268                    exp.and_(expression.is_(exp.Null()), search.is_(exp.Null()), copy=False),
    5269                    copy=False,
    5270                )
    5271                ifs.append(exp.If(this=cond, true=result))
    5272
    5273        case = exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
    5274        return self.sql(case)

def semanticview_sql(self, expression: sqlglot.expressions.SemanticView) -> str: View Source

    5276    def semanticview_sql(self, expression: exp.SemanticView) -> str:
    5277        this = self.sql(expression, "this")
    5278        this = self.seg(this, sep="")
    5279        dimensions = self.expressions(
    5280            expression, "dimensions", dynamic=True, skip_first=True, skip_last=True
    5281        )
    5282        dimensions = self.seg(f"DIMENSIONS {dimensions}") if dimensions else ""
    5283        metrics = self.expressions(
    5284            expression, "metrics", dynamic=True, skip_first=True, skip_last=True
    5285        )
    5286        metrics = self.seg(f"METRICS {metrics}") if metrics else ""
    5287        where = self.sql(expression, "where")
    5288        where = self.seg(f"WHERE {where}") if where else ""
    5289        body = self.indent(this + metrics + dimensions + where, skip_first=True)
    5290        return f"SEMANTIC_VIEW({body}{self.seg(')', sep='')}"

def getextract_sql(self, expression: sqlglot.expressions.GetExtract) -> str: View Source

    5292    def getextract_sql(self, expression: exp.GetExtract) -> str:
    5293        this = expression.this
    5294        expr = expression.expression
    5295
    5296        if not this.type or not expression.type:
    5297            from sqlglot.optimizer.annotate_types import annotate_types
    5298
    5299            this = annotate_types(this, dialect=self.dialect)
    5300
    5301        if this.is_type(*(exp.DataType.Type.ARRAY, exp.DataType.Type.MAP)):
    5302            return self.sql(exp.Bracket(this=this, expressions=[expr]))
    5303
    5304        return self.sql(exp.JSONExtract(this=this, expression=self.dialect.to_json_path(expr)))

def datefromunixdate_sql(self, expression: sqlglot.expressions.DateFromUnixDate) -> str: View Source

    5306    def datefromunixdate_sql(self, expression: exp.DateFromUnixDate) -> str:
    5307        return self.sql(
    5308            exp.DateAdd(
    5309                this=exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    5310                expression=expression.this,
    5311                unit=exp.var("DAY"),
    5312            )
    5313        )

def space_sql( self: Generator, expression: sqlglot.expressions.Space) -> str: View Source

    5315    def space_sql(self: Generator, expression: exp.Space) -> str:
    5316        return self.sql(exp.Repeat(this=exp.Literal.string(" "), times=expression.this))

def buildproperty_sql(self, expression: sqlglot.expressions.BuildProperty) -> str: View Source

    5318    def buildproperty_sql(self, expression: exp.BuildProperty) -> str:
    5319        return f"BUILD {self.sql(expression, 'this')}"

def refreshtriggerproperty_sql(self, expression: sqlglot.expressions.RefreshTriggerProperty) -> str: View Source

    5321    def refreshtriggerproperty_sql(self, expression: exp.RefreshTriggerProperty) -> str:
    5322        method = self.sql(expression, "method")
    5323        kind = expression.args.get("kind")
    5324        if not kind:
    5325            return f"REFRESH {method}"
    5326
    5327        every = self.sql(expression, "every")
    5328        unit = self.sql(expression, "unit")
    5329        every = f" EVERY {every} {unit}" if every else ""
    5330        starts = self.sql(expression, "starts")
    5331        starts = f" STARTS {starts}" if starts else ""
    5332
    5333        return f"REFRESH {method} ON {kind}{every}{starts}"

def modelattribute_sql(self, expression: sqlglot.expressions.ModelAttribute) -> str: View Source

    5335    def modelattribute_sql(self, expression: exp.ModelAttribute) -> str:
    5336        self.unsupported("The model!attribute syntax is not supported")
    5337        return ""