Edit on GitHub

sqlglot.generator

   1from __future__ import annotations
   2
   3import logging
   4import re
   5import typing as t
   6from collections import defaultdict
   7from functools import reduce, wraps
   8
   9from sqlglot import exp
  10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
  11from sqlglot.helper import apply_index_offset, csv, name_sequence, seq_get
  12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
  13from sqlglot.time import format_time
  14from sqlglot.tokens import TokenType
  15
  16if t.TYPE_CHECKING:
  17    from sqlglot._typing import E
  18    from sqlglot.dialects.dialect import DialectType
  19
  20    G = t.TypeVar("G", bound="Generator")
  21    GeneratorMethod = t.Callable[[G, E], str]
  22
  23logger = logging.getLogger("sqlglot")
  24
  25ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
  26UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
  27
  28
  29def unsupported_args(
  30    *args: t.Union[str, t.Tuple[str, str]],
  31) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
  32    """
  33    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
  34    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
  35    """
  36    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
  37    for arg in args:
  38        if isinstance(arg, str):
  39            diagnostic_by_arg[arg] = None
  40        else:
  41            diagnostic_by_arg[arg[0]] = arg[1]
  42
  43    def decorator(func: GeneratorMethod) -> GeneratorMethod:
  44        @wraps(func)
  45        def _func(generator: G, expression: E) -> str:
  46            expression_name = expression.__class__.__name__
  47            dialect_name = generator.dialect.__class__.__name__
  48
  49            for arg_name, diagnostic in diagnostic_by_arg.items():
  50                if expression.args.get(arg_name):
  51                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
  52                        arg_name, expression_name, dialect_name
  53                    )
  54                    generator.unsupported(diagnostic)
  55
  56            return func(generator, expression)
  57
  58        return _func
  59
  60    return decorator
  61
  62
  63class _Generator(type):
  64    def __new__(cls, clsname, bases, attrs):
  65        klass = super().__new__(cls, clsname, bases, attrs)
  66
  67        # Remove transforms that correspond to unsupported JSONPathPart expressions
  68        for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS:
  69            klass.TRANSFORMS.pop(part, None)
  70
  71        return klass
  72
  73
  74class Generator(metaclass=_Generator):
  75    """
  76    Generator converts a given syntax tree to the corresponding SQL string.
  77
  78    Args:
  79        pretty: Whether to format the produced SQL string.
  80            Default: False.
  81        identify: Determines when an identifier should be quoted. Possible values are:
  82            False (default): Never quote, except in cases where it's mandatory by the dialect.
  83            True or 'always': Always quote.
  84            'safe': Only quote identifiers that are case insensitive.
  85        normalize: Whether to normalize identifiers to lowercase.
  86            Default: False.
  87        pad: The pad size in a formatted string. For example, this affects the indentation of
  88            a projection in a query, relative to its nesting level.
  89            Default: 2.
  90        indent: The indentation size in a formatted string. For example, this affects the
  91            indentation of subqueries and filters under a `WHERE` clause.
  92            Default: 2.
  93        normalize_functions: How to normalize function names. Possible values are:
  94            "upper" or True (default): Convert names to uppercase.
  95            "lower": Convert names to lowercase.
  96            False: Disables function name normalization.
  97        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
  98            Default ErrorLevel.WARN.
  99        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
 100            This is only relevant if unsupported_level is ErrorLevel.RAISE.
 101            Default: 3
 102        leading_comma: Whether the comma is leading or trailing in select expressions.
 103            This is only relevant when generating in pretty mode.
 104            Default: False
 105        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
 106            The default is on the smaller end because the length only represents a segment and not the true
 107            line length.
 108            Default: 80
 109        comments: Whether to preserve comments in the output SQL code.
 110            Default: True
 111    """
 112
 113    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
 114        **JSON_PATH_PART_TRANSFORMS,
 115        exp.AllowedValuesProperty: lambda self,
 116        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
 117        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
 118        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
 119        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
 120        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
 121        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
 122        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
 123        exp.CaseSpecificColumnConstraint: lambda _,
 124        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
 125        exp.Ceil: lambda self, e: self.ceil_floor(e),
 126        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
 127        exp.CharacterSetProperty: lambda self,
 128        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
 129        exp.ClusteredColumnConstraint: lambda self,
 130        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
 131        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
 132        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
 133        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
 134        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
 135        exp.CredentialsProperty: lambda self,
 136        e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
 137        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
 138        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
 139        exp.DynamicProperty: lambda *_: "DYNAMIC",
 140        exp.EmptyProperty: lambda *_: "EMPTY",
 141        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
 142        exp.EphemeralColumnConstraint: lambda self,
 143        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
 144        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
 145        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
 146        exp.Except: lambda self, e: self.set_operations(e),
 147        exp.ExternalProperty: lambda *_: "EXTERNAL",
 148        exp.Floor: lambda self, e: self.ceil_floor(e),
 149        exp.Get: lambda self, e: self.get_put_sql(e),
 150        exp.GlobalProperty: lambda *_: "GLOBAL",
 151        exp.HeapProperty: lambda *_: "HEAP",
 152        exp.IcebergProperty: lambda *_: "ICEBERG",
 153        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
 154        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
 155        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
 156        exp.Intersect: lambda self, e: self.set_operations(e),
 157        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
 158        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
 159        exp.LanguageProperty: lambda self, e: self.naked_property(e),
 160        exp.LocationProperty: lambda self, e: self.naked_property(e),
 161        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 162        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
 163        exp.NonClusteredColumnConstraint: lambda self,
 164        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 165        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
 166        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
 167        exp.OnCommitProperty: lambda _,
 168        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
 169        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
 170        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
 171        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
 172        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
 173        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
 174        exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
 175        exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
 176        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
 177        exp.ProjectionPolicyColumnConstraint: lambda self,
 178        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
 179        exp.Put: lambda self, e: self.get_put_sql(e),
 180        exp.RemoteWithConnectionModelProperty: lambda self,
 181        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
 182        exp.ReturnsProperty: lambda self, e: (
 183            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
 184        ),
 185        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
 186        exp.SecureProperty: lambda *_: "SECURE",
 187        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
 188        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
 189        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
 190        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
 191        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
 192        exp.SqlReadWriteProperty: lambda _, e: e.name,
 193        exp.SqlSecurityProperty: lambda _,
 194        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 195        exp.StabilityProperty: lambda _, e: e.name,
 196        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
 197        exp.StreamingTableProperty: lambda *_: "STREAMING",
 198        exp.StrictProperty: lambda *_: "STRICT",
 199        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
 200        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
 201        exp.TemporaryProperty: lambda *_: "TEMPORARY",
 202        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
 203        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
 204        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
 205        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
 206        exp.TransientProperty: lambda *_: "TRANSIENT",
 207        exp.Union: lambda self, e: self.set_operations(e),
 208        exp.UnloggedProperty: lambda *_: "UNLOGGED",
 209        exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
 210        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
 211        exp.Uuid: lambda *_: "UUID()",
 212        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
 213        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 214        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
 215        exp.VolatileProperty: lambda *_: "VOLATILE",
 216        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
 217        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
 218        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
 219        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
 220        exp.ForceProperty: lambda *_: "FORCE",
 221    }
 222
 223    # Whether null ordering is supported in order by
 224    # True: Full Support, None: No support, False: No support for certain cases
 225    # such as window specifications, aggregate functions etc
 226    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
 227
 228    # Whether ignore nulls is inside the agg or outside.
 229    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
 230    IGNORE_NULLS_IN_FUNC = False
 231
 232    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 233    LOCKING_READS_SUPPORTED = False
 234
 235    # Whether the EXCEPT and INTERSECT operations can return duplicates
 236    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
 237
 238    # Wrap derived values in parens, usually standard but spark doesn't support it
 239    WRAP_DERIVED_VALUES = True
 240
 241    # Whether create function uses an AS before the RETURN
 242    CREATE_FUNCTION_RETURN_AS = True
 243
 244    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
 245    MATCHED_BY_SOURCE = True
 246
 247    # Whether the INTERVAL expression works only with values like '1 day'
 248    SINGLE_STRING_INTERVAL = False
 249
 250    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
 251    INTERVAL_ALLOWS_PLURAL_FORM = True
 252
 253    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
 254    LIMIT_FETCH = "ALL"
 255
 256    # Whether limit and fetch allows expresions or just limits
 257    LIMIT_ONLY_LITERALS = False
 258
 259    # Whether a table is allowed to be renamed with a db
 260    RENAME_TABLE_WITH_DB = True
 261
 262    # The separator for grouping sets and rollups
 263    GROUPINGS_SEP = ","
 264
 265    # The string used for creating an index on a table
 266    INDEX_ON = "ON"
 267
 268    # Whether join hints should be generated
 269    JOIN_HINTS = True
 270
 271    # Whether table hints should be generated
 272    TABLE_HINTS = True
 273
 274    # Whether query hints should be generated
 275    QUERY_HINTS = True
 276
 277    # What kind of separator to use for query hints
 278    QUERY_HINT_SEP = ", "
 279
 280    # Whether comparing against booleans (e.g. x IS TRUE) is supported
 281    IS_BOOL_ALLOWED = True
 282
 283    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
 284    DUPLICATE_KEY_UPDATE_WITH_SET = True
 285
 286    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
 287    LIMIT_IS_TOP = False
 288
 289    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
 290    RETURNING_END = True
 291
 292    # Whether to generate an unquoted value for EXTRACT's date part argument
 293    EXTRACT_ALLOWS_QUOTES = True
 294
 295    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
 296    TZ_TO_WITH_TIME_ZONE = False
 297
 298    # Whether the NVL2 function is supported
 299    NVL2_SUPPORTED = True
 300
 301    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
 302    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
 303
 304    # Whether VALUES statements can be used as derived tables.
 305    # MySQL 5 and Redshift do not allow this, so when False, it will convert
 306    # SELECT * VALUES into SELECT UNION
 307    VALUES_AS_TABLE = True
 308
 309    # Whether the word COLUMN is included when adding a column with ALTER TABLE
 310    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
 311
 312    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
 313    UNNEST_WITH_ORDINALITY = True
 314
 315    # Whether FILTER (WHERE cond) can be used for conditional aggregation
 316    AGGREGATE_FILTER_SUPPORTED = True
 317
 318    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
 319    SEMI_ANTI_JOIN_WITH_SIDE = True
 320
 321    # Whether to include the type of a computed column in the CREATE DDL
 322    COMPUTED_COLUMN_WITH_TYPE = True
 323
 324    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
 325    SUPPORTS_TABLE_COPY = True
 326
 327    # Whether parentheses are required around the table sample's expression
 328    TABLESAMPLE_REQUIRES_PARENS = True
 329
 330    # Whether a table sample clause's size needs to be followed by the ROWS keyword
 331    TABLESAMPLE_SIZE_IS_ROWS = True
 332
 333    # The keyword(s) to use when generating a sample clause
 334    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
 335
 336    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
 337    TABLESAMPLE_WITH_METHOD = True
 338
 339    # The keyword to use when specifying the seed of a sample clause
 340    TABLESAMPLE_SEED_KEYWORD = "SEED"
 341
 342    # Whether COLLATE is a function instead of a binary operator
 343    COLLATE_IS_FUNC = False
 344
 345    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
 346    DATA_TYPE_SPECIFIERS_ALLOWED = False
 347
 348    # Whether conditions require booleans WHERE x = 0 vs WHERE x
 349    ENSURE_BOOLS = False
 350
 351    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
 352    CTE_RECURSIVE_KEYWORD_REQUIRED = True
 353
 354    # Whether CONCAT requires >1 arguments
 355    SUPPORTS_SINGLE_ARG_CONCAT = True
 356
 357    # Whether LAST_DAY function supports a date part argument
 358    LAST_DAY_SUPPORTS_DATE_PART = True
 359
 360    # Whether named columns are allowed in table aliases
 361    SUPPORTS_TABLE_ALIAS_COLUMNS = True
 362
 363    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
 364    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
 365
 366    # What delimiter to use for separating JSON key/value pairs
 367    JSON_KEY_VALUE_PAIR_SEP = ":"
 368
 369    # INSERT OVERWRITE TABLE x override
 370    INSERT_OVERWRITE = " OVERWRITE TABLE"
 371
 372    # Whether the SELECT .. INTO syntax is used instead of CTAS
 373    SUPPORTS_SELECT_INTO = False
 374
 375    # Whether UNLOGGED tables can be created
 376    SUPPORTS_UNLOGGED_TABLES = False
 377
 378    # Whether the CREATE TABLE LIKE statement is supported
 379    SUPPORTS_CREATE_TABLE_LIKE = True
 380
 381    # Whether the LikeProperty needs to be specified inside of the schema clause
 382    LIKE_PROPERTY_INSIDE_SCHEMA = False
 383
 384    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
 385    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
 386    MULTI_ARG_DISTINCT = True
 387
 388    # Whether the JSON extraction operators expect a value of type JSON
 389    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
 390
 391    # Whether bracketed keys like ["foo"] are supported in JSON paths
 392    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
 393
 394    # Whether to escape keys using single quotes in JSON paths
 395    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
 396
 397    # The JSONPathPart expressions supported by this dialect
 398    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
 399
 400    # Whether any(f(x) for x in array) can be implemented by this dialect
 401    CAN_IMPLEMENT_ARRAY_ANY = False
 402
 403    # Whether the function TO_NUMBER is supported
 404    SUPPORTS_TO_NUMBER = True
 405
 406    # Whether or not set op modifiers apply to the outer set op or select.
 407    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
 408    # True means limit 1 happens after the set op, False means it it happens on y.
 409    SET_OP_MODIFIERS = True
 410
 411    # Whether parameters from COPY statement are wrapped in parentheses
 412    COPY_PARAMS_ARE_WRAPPED = True
 413
 414    # Whether values of params are set with "=" token or empty space
 415    COPY_PARAMS_EQ_REQUIRED = False
 416
 417    # Whether COPY statement has INTO keyword
 418    COPY_HAS_INTO_KEYWORD = True
 419
 420    # Whether the conditional TRY(expression) function is supported
 421    TRY_SUPPORTED = True
 422
 423    # Whether the UESCAPE syntax in unicode strings is supported
 424    SUPPORTS_UESCAPE = True
 425
 426    # The keyword to use when generating a star projection with excluded columns
 427    STAR_EXCEPT = "EXCEPT"
 428
 429    # The HEX function name
 430    HEX_FUNC = "HEX"
 431
 432    # The keywords to use when prefixing & separating WITH based properties
 433    WITH_PROPERTIES_PREFIX = "WITH"
 434
 435    # Whether to quote the generated expression of exp.JsonPath
 436    QUOTE_JSON_PATH = True
 437
 438    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
 439    PAD_FILL_PATTERN_IS_REQUIRED = False
 440
 441    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
 442    SUPPORTS_EXPLODING_PROJECTIONS = True
 443
 444    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
 445    ARRAY_CONCAT_IS_VAR_LEN = True
 446
 447    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
 448    SUPPORTS_CONVERT_TIMEZONE = False
 449
 450    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
 451    SUPPORTS_MEDIAN = True
 452
 453    # Whether UNIX_SECONDS(timestamp) is supported
 454    SUPPORTS_UNIX_SECONDS = False
 455
 456    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
 457    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
 458
 459    # The function name of the exp.ArraySize expression
 460    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
 461
 462    # The syntax to use when altering the type of a column
 463    ALTER_SET_TYPE = "SET DATA TYPE"
 464
 465    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
 466    # None -> Doesn't support it at all
 467    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
 468    # True (Postgres) -> Explicitly requires it
 469    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
 470
 471    TYPE_MAPPING = {
 472        exp.DataType.Type.DATETIME2: "TIMESTAMP",
 473        exp.DataType.Type.NCHAR: "CHAR",
 474        exp.DataType.Type.NVARCHAR: "VARCHAR",
 475        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 476        exp.DataType.Type.LONGTEXT: "TEXT",
 477        exp.DataType.Type.TINYTEXT: "TEXT",
 478        exp.DataType.Type.BLOB: "VARBINARY",
 479        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 480        exp.DataType.Type.LONGBLOB: "BLOB",
 481        exp.DataType.Type.TINYBLOB: "BLOB",
 482        exp.DataType.Type.INET: "INET",
 483        exp.DataType.Type.ROWVERSION: "VARBINARY",
 484        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
 485    }
 486
 487    TIME_PART_SINGULARS = {
 488        "MICROSECONDS": "MICROSECOND",
 489        "SECONDS": "SECOND",
 490        "MINUTES": "MINUTE",
 491        "HOURS": "HOUR",
 492        "DAYS": "DAY",
 493        "WEEKS": "WEEK",
 494        "MONTHS": "MONTH",
 495        "QUARTERS": "QUARTER",
 496        "YEARS": "YEAR",
 497    }
 498
 499    AFTER_HAVING_MODIFIER_TRANSFORMS = {
 500        "cluster": lambda self, e: self.sql(e, "cluster"),
 501        "distribute": lambda self, e: self.sql(e, "distribute"),
 502        "sort": lambda self, e: self.sql(e, "sort"),
 503        "windows": lambda self, e: (
 504            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
 505            if e.args.get("windows")
 506            else ""
 507        ),
 508        "qualify": lambda self, e: self.sql(e, "qualify"),
 509    }
 510
 511    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 512
 513    STRUCT_DELIMITER = ("<", ">")
 514
 515    PARAMETER_TOKEN = "@"
 516    NAMED_PLACEHOLDER_TOKEN = ":"
 517
 518    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
 519
 520    PROPERTIES_LOCATION = {
 521        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
 522        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 523        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 524        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
 525        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
 526        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 527        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 528        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 529        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 530        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
 531        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 532        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
 533        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
 534        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
 535        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 536        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
 537        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 538        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
 539        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
 540        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
 541        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 542        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 543        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
 544        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
 545        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 546        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 547        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 548        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 549        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 550        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 551        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
 552        exp.HeapProperty: exp.Properties.Location.POST_WITH,
 553        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
 554        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
 555        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
 556        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
 557        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 558        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 559        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 560        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 561        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 562        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
 563        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 564        exp.LogProperty: exp.Properties.Location.POST_NAME,
 565        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 566        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 567        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 568        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 569        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 570        exp.Order: exp.Properties.Location.POST_SCHEMA,
 571        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 572        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 573        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 574        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 575        exp.Property: exp.Properties.Location.POST_WITH,
 576        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 577        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 578        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 579        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 580        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 581        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 582        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 583        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
 584        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
 585        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 586        exp.Set: exp.Properties.Location.POST_SCHEMA,
 587        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 588        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 589        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 590        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
 591        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
 592        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 593        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 594        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 595        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 596        exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
 597        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
 598        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
 599        exp.Tags: exp.Properties.Location.POST_WITH,
 600        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 601        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 602        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 603        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 604        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 605        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
 606        exp.UsingTemplateProperty: exp.Properties.Location.POST_SCHEMA,
 607        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
 608        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 609        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 610        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 611        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
 612        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
 613        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 614        exp.ForceProperty: exp.Properties.Location.POST_CREATE,
 615    }
 616
 617    # Keywords that can't be used as unquoted identifier names
 618    RESERVED_KEYWORDS: t.Set[str] = set()
 619
 620    # Expressions whose comments are separated from them for better formatting
 621    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 622        exp.Command,
 623        exp.Create,
 624        exp.Describe,
 625        exp.Delete,
 626        exp.Drop,
 627        exp.From,
 628        exp.Insert,
 629        exp.Join,
 630        exp.MultitableInserts,
 631        exp.Select,
 632        exp.SetOperation,
 633        exp.Update,
 634        exp.Where,
 635        exp.With,
 636    )
 637
 638    # Expressions that should not have their comments generated in maybe_comment
 639    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 640        exp.Binary,
 641        exp.SetOperation,
 642    )
 643
 644    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 645    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 646        exp.Column,
 647        exp.Literal,
 648        exp.Neg,
 649        exp.Paren,
 650    )
 651
 652    PARAMETERIZABLE_TEXT_TYPES = {
 653        exp.DataType.Type.NVARCHAR,
 654        exp.DataType.Type.VARCHAR,
 655        exp.DataType.Type.CHAR,
 656        exp.DataType.Type.NCHAR,
 657    }
 658
 659    # Expressions that need to have all CTEs under them bubbled up to them
 660    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 661
 662    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 663
 664    __slots__ = (
 665        "pretty",
 666        "identify",
 667        "normalize",
 668        "pad",
 669        "_indent",
 670        "normalize_functions",
 671        "unsupported_level",
 672        "max_unsupported",
 673        "leading_comma",
 674        "max_text_width",
 675        "comments",
 676        "dialect",
 677        "unsupported_messages",
 678        "_escaped_quote_end",
 679        "_escaped_identifier_end",
 680        "_next_name",
 681        "_identifier_start",
 682        "_identifier_end",
 683        "_quote_json_path_key_using_brackets",
 684    )
 685
 686    def __init__(
 687        self,
 688        pretty: t.Optional[bool] = None,
 689        identify: str | bool = False,
 690        normalize: bool = False,
 691        pad: int = 2,
 692        indent: int = 2,
 693        normalize_functions: t.Optional[str | bool] = None,
 694        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 695        max_unsupported: int = 3,
 696        leading_comma: bool = False,
 697        max_text_width: int = 80,
 698        comments: bool = True,
 699        dialect: DialectType = None,
 700    ):
 701        import sqlglot
 702        from sqlglot.dialects import Dialect
 703
 704        self.pretty = pretty if pretty is not None else sqlglot.pretty
 705        self.identify = identify
 706        self.normalize = normalize
 707        self.pad = pad
 708        self._indent = indent
 709        self.unsupported_level = unsupported_level
 710        self.max_unsupported = max_unsupported
 711        self.leading_comma = leading_comma
 712        self.max_text_width = max_text_width
 713        self.comments = comments
 714        self.dialect = Dialect.get_or_raise(dialect)
 715
 716        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 717        self.normalize_functions = (
 718            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 719        )
 720
 721        self.unsupported_messages: t.List[str] = []
 722        self._escaped_quote_end: str = (
 723            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 724        )
 725        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
 726
 727        self._next_name = name_sequence("_t")
 728
 729        self._identifier_start = self.dialect.IDENTIFIER_START
 730        self._identifier_end = self.dialect.IDENTIFIER_END
 731
 732        self._quote_json_path_key_using_brackets = True
 733
 734    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 735        """
 736        Generates the SQL string corresponding to the given syntax tree.
 737
 738        Args:
 739            expression: The syntax tree.
 740            copy: Whether to copy the expression. The generator performs mutations so
 741                it is safer to copy.
 742
 743        Returns:
 744            The SQL string corresponding to `expression`.
 745        """
 746        if copy:
 747            expression = expression.copy()
 748
 749        expression = self.preprocess(expression)
 750
 751        self.unsupported_messages = []
 752        sql = self.sql(expression).strip()
 753
 754        if self.pretty:
 755            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 756
 757        if self.unsupported_level == ErrorLevel.IGNORE:
 758            return sql
 759
 760        if self.unsupported_level == ErrorLevel.WARN:
 761            for msg in self.unsupported_messages:
 762                logger.warning(msg)
 763        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 764            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 765
 766        return sql
 767
 768    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 769        """Apply generic preprocessing transformations to a given expression."""
 770        expression = self._move_ctes_to_top_level(expression)
 771
 772        if self.ENSURE_BOOLS:
 773            from sqlglot.transforms import ensure_bools
 774
 775            expression = ensure_bools(expression)
 776
 777        return expression
 778
 779    def _move_ctes_to_top_level(self, expression: E) -> E:
 780        if (
 781            not expression.parent
 782            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 783            and any(node.parent is not expression for node in expression.find_all(exp.With))
 784        ):
 785            from sqlglot.transforms import move_ctes_to_top_level
 786
 787            expression = move_ctes_to_top_level(expression)
 788        return expression
 789
 790    def unsupported(self, message: str) -> None:
 791        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 792            raise UnsupportedError(message)
 793        self.unsupported_messages.append(message)
 794
 795    def sep(self, sep: str = " ") -> str:
 796        return f"{sep.strip()}\n" if self.pretty else sep
 797
 798    def seg(self, sql: str, sep: str = " ") -> str:
 799        return f"{self.sep(sep)}{sql}"
 800
 801    def pad_comment(self, comment: str) -> str:
 802        comment = " " + comment if comment[0].strip() else comment
 803        comment = comment + " " if comment[-1].strip() else comment
 804        return comment
 805
 806    def maybe_comment(
 807        self,
 808        sql: str,
 809        expression: t.Optional[exp.Expression] = None,
 810        comments: t.Optional[t.List[str]] = None,
 811        separated: bool = False,
 812    ) -> str:
 813        comments = (
 814            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 815            if self.comments
 816            else None
 817        )
 818
 819        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 820            return sql
 821
 822        comments_sql = " ".join(
 823            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 824        )
 825
 826        if not comments_sql:
 827            return sql
 828
 829        comments_sql = self._replace_line_breaks(comments_sql)
 830
 831        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 832            return (
 833                f"{self.sep()}{comments_sql}{sql}"
 834                if not sql or sql[0].isspace()
 835                else f"{comments_sql}{self.sep()}{sql}"
 836            )
 837
 838        return f"{sql} {comments_sql}"
 839
 840    def wrap(self, expression: exp.Expression | str) -> str:
 841        this_sql = (
 842            self.sql(expression)
 843            if isinstance(expression, exp.UNWRAPPED_QUERIES)
 844            else self.sql(expression, "this")
 845        )
 846        if not this_sql:
 847            return "()"
 848
 849        this_sql = self.indent(this_sql, level=1, pad=0)
 850        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 851
 852    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 853        original = self.identify
 854        self.identify = False
 855        result = func(*args, **kwargs)
 856        self.identify = original
 857        return result
 858
 859    def normalize_func(self, name: str) -> str:
 860        if self.normalize_functions == "upper" or self.normalize_functions is True:
 861            return name.upper()
 862        if self.normalize_functions == "lower":
 863            return name.lower()
 864        return name
 865
 866    def indent(
 867        self,
 868        sql: str,
 869        level: int = 0,
 870        pad: t.Optional[int] = None,
 871        skip_first: bool = False,
 872        skip_last: bool = False,
 873    ) -> str:
 874        if not self.pretty or not sql:
 875            return sql
 876
 877        pad = self.pad if pad is None else pad
 878        lines = sql.split("\n")
 879
 880        return "\n".join(
 881            (
 882                line
 883                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 884                else f"{' ' * (level * self._indent + pad)}{line}"
 885            )
 886            for i, line in enumerate(lines)
 887        )
 888
 889    def sql(
 890        self,
 891        expression: t.Optional[str | exp.Expression],
 892        key: t.Optional[str] = None,
 893        comment: bool = True,
 894    ) -> str:
 895        if not expression:
 896            return ""
 897
 898        if isinstance(expression, str):
 899            return expression
 900
 901        if key:
 902            value = expression.args.get(key)
 903            if value:
 904                return self.sql(value)
 905            return ""
 906
 907        transform = self.TRANSFORMS.get(expression.__class__)
 908
 909        if callable(transform):
 910            sql = transform(self, expression)
 911        elif isinstance(expression, exp.Expression):
 912            exp_handler_name = f"{expression.key}_sql"
 913
 914            if hasattr(self, exp_handler_name):
 915                sql = getattr(self, exp_handler_name)(expression)
 916            elif isinstance(expression, exp.Func):
 917                sql = self.function_fallback_sql(expression)
 918            elif isinstance(expression, exp.Property):
 919                sql = self.property_sql(expression)
 920            else:
 921                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 922        else:
 923            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 924
 925        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 926
 927    def uncache_sql(self, expression: exp.Uncache) -> str:
 928        table = self.sql(expression, "this")
 929        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 930        return f"UNCACHE TABLE{exists_sql} {table}"
 931
 932    def cache_sql(self, expression: exp.Cache) -> str:
 933        lazy = " LAZY" if expression.args.get("lazy") else ""
 934        table = self.sql(expression, "this")
 935        options = expression.args.get("options")
 936        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 937        sql = self.sql(expression, "expression")
 938        sql = f" AS{self.sep()}{sql}" if sql else ""
 939        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 940        return self.prepend_ctes(expression, sql)
 941
 942    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 943        if isinstance(expression.parent, exp.Cast):
 944            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 945        default = "DEFAULT " if expression.args.get("default") else ""
 946        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 947
 948    def column_parts(self, expression: exp.Column) -> str:
 949        return ".".join(
 950            self.sql(part)
 951            for part in (
 952                expression.args.get("catalog"),
 953                expression.args.get("db"),
 954                expression.args.get("table"),
 955                expression.args.get("this"),
 956            )
 957            if part
 958        )
 959
 960    def column_sql(self, expression: exp.Column) -> str:
 961        join_mark = " (+)" if expression.args.get("join_mark") else ""
 962
 963        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
 964            join_mark = ""
 965            self.unsupported("Outer join syntax using the (+) operator is not supported.")
 966
 967        return f"{self.column_parts(expression)}{join_mark}"
 968
 969    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
 970        this = self.sql(expression, "this")
 971        this = f" {this}" if this else ""
 972        position = self.sql(expression, "position")
 973        return f"{position}{this}"
 974
 975    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
 976        column = self.sql(expression, "this")
 977        kind = self.sql(expression, "kind")
 978        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 979        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 980        kind = f"{sep}{kind}" if kind else ""
 981        constraints = f" {constraints}" if constraints else ""
 982        position = self.sql(expression, "position")
 983        position = f" {position}" if position else ""
 984
 985        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
 986            kind = ""
 987
 988        return f"{exists}{column}{kind}{constraints}{position}"
 989
 990    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 991        this = self.sql(expression, "this")
 992        kind_sql = self.sql(expression, "kind").strip()
 993        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 994
 995    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
 996        this = self.sql(expression, "this")
 997        if expression.args.get("not_null"):
 998            persisted = " PERSISTED NOT NULL"
 999        elif expression.args.get("persisted"):
1000            persisted = " PERSISTED"
1001        else:
1002            persisted = ""
1003        return f"AS {this}{persisted}"
1004
1005    def autoincrementcolumnconstraint_sql(self, _) -> str:
1006        return self.token_sql(TokenType.AUTO_INCREMENT)
1007
1008    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
1009        if isinstance(expression.this, list):
1010            this = self.wrap(self.expressions(expression, key="this", flat=True))
1011        else:
1012            this = self.sql(expression, "this")
1013
1014        return f"COMPRESS {this}"
1015
1016    def generatedasidentitycolumnconstraint_sql(
1017        self, expression: exp.GeneratedAsIdentityColumnConstraint
1018    ) -> str:
1019        this = ""
1020        if expression.this is not None:
1021            on_null = " ON NULL" if expression.args.get("on_null") else ""
1022            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
1023
1024        start = expression.args.get("start")
1025        start = f"START WITH {start}" if start else ""
1026        increment = expression.args.get("increment")
1027        increment = f" INCREMENT BY {increment}" if increment else ""
1028        minvalue = expression.args.get("minvalue")
1029        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1030        maxvalue = expression.args.get("maxvalue")
1031        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1032        cycle = expression.args.get("cycle")
1033        cycle_sql = ""
1034
1035        if cycle is not None:
1036            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
1037            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
1038
1039        sequence_opts = ""
1040        if start or increment or cycle_sql:
1041            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
1042            sequence_opts = f" ({sequence_opts.strip()})"
1043
1044        expr = self.sql(expression, "expression")
1045        expr = f"({expr})" if expr else "IDENTITY"
1046
1047        return f"GENERATED{this} AS {expr}{sequence_opts}"
1048
1049    def generatedasrowcolumnconstraint_sql(
1050        self, expression: exp.GeneratedAsRowColumnConstraint
1051    ) -> str:
1052        start = "START" if expression.args.get("start") else "END"
1053        hidden = " HIDDEN" if expression.args.get("hidden") else ""
1054        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
1055
1056    def periodforsystemtimeconstraint_sql(
1057        self, expression: exp.PeriodForSystemTimeConstraint
1058    ) -> str:
1059        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
1060
1061    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
1062        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
1063
1064    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
1065        return f"AS {self.sql(expression, 'this')}"
1066
1067    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
1068        desc = expression.args.get("desc")
1069        if desc is not None:
1070            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1071        options = self.expressions(expression, key="options", flat=True, sep=" ")
1072        options = f" {options}" if options else ""
1073        return f"PRIMARY KEY{options}"
1074
1075    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1076        this = self.sql(expression, "this")
1077        this = f" {this}" if this else ""
1078        index_type = expression.args.get("index_type")
1079        index_type = f" USING {index_type}" if index_type else ""
1080        on_conflict = self.sql(expression, "on_conflict")
1081        on_conflict = f" {on_conflict}" if on_conflict else ""
1082        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1083        options = self.expressions(expression, key="options", flat=True, sep=" ")
1084        options = f" {options}" if options else ""
1085        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
1086
1087    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
1088        return self.sql(expression, "this")
1089
1090    def create_sql(self, expression: exp.Create) -> str:
1091        kind = self.sql(expression, "kind")
1092        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1093        properties = expression.args.get("properties")
1094        properties_locs = self.locate_properties(properties) if properties else defaultdict()
1095
1096        this = self.createable_sql(expression, properties_locs)
1097
1098        properties_sql = ""
1099        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
1100            exp.Properties.Location.POST_WITH
1101        ):
1102            properties_sql = self.sql(
1103                exp.Properties(
1104                    expressions=[
1105                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
1106                        *properties_locs[exp.Properties.Location.POST_WITH],
1107                    ]
1108                )
1109            )
1110
1111            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
1112                properties_sql = self.sep() + properties_sql
1113            elif not self.pretty:
1114                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
1115                properties_sql = f" {properties_sql}"
1116
1117        begin = " BEGIN" if expression.args.get("begin") else ""
1118        end = " END" if expression.args.get("end") else ""
1119
1120        expression_sql = self.sql(expression, "expression")
1121        if expression_sql:
1122            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
1123
1124            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1125                postalias_props_sql = ""
1126                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1127                    postalias_props_sql = self.properties(
1128                        exp.Properties(
1129                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
1130                        ),
1131                        wrapped=False,
1132                    )
1133                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
1134                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
1135
1136        postindex_props_sql = ""
1137        if properties_locs.get(exp.Properties.Location.POST_INDEX):
1138            postindex_props_sql = self.properties(
1139                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
1140                wrapped=False,
1141                prefix=" ",
1142            )
1143
1144        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
1145        indexes = f" {indexes}" if indexes else ""
1146        index_sql = indexes + postindex_props_sql
1147
1148        replace = " OR REPLACE" if expression.args.get("replace") else ""
1149        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
1150        unique = " UNIQUE" if expression.args.get("unique") else ""
1151
1152        clustered = expression.args.get("clustered")
1153        if clustered is None:
1154            clustered_sql = ""
1155        elif clustered:
1156            clustered_sql = " CLUSTERED COLUMNSTORE"
1157        else:
1158            clustered_sql = " NONCLUSTERED COLUMNSTORE"
1159
1160        postcreate_props_sql = ""
1161        if properties_locs.get(exp.Properties.Location.POST_CREATE):
1162            postcreate_props_sql = self.properties(
1163                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
1164                sep=" ",
1165                prefix=" ",
1166                wrapped=False,
1167            )
1168
1169        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
1170
1171        postexpression_props_sql = ""
1172        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
1173            postexpression_props_sql = self.properties(
1174                exp.Properties(
1175                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
1176                ),
1177                sep=" ",
1178                prefix=" ",
1179                wrapped=False,
1180            )
1181
1182        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1183        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
1184        no_schema_binding = (
1185            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
1186        )
1187
1188        clone = self.sql(expression, "clone")
1189        clone = f" {clone}" if clone else ""
1190
1191        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
1192            properties_expression = f"{expression_sql}{properties_sql}"
1193        else:
1194            properties_expression = f"{properties_sql}{expression_sql}"
1195
1196        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
1197        return self.prepend_ctes(expression, expression_sql)
1198
1199    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
1200        start = self.sql(expression, "start")
1201        start = f"START WITH {start}" if start else ""
1202        increment = self.sql(expression, "increment")
1203        increment = f" INCREMENT BY {increment}" if increment else ""
1204        minvalue = self.sql(expression, "minvalue")
1205        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1206        maxvalue = self.sql(expression, "maxvalue")
1207        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1208        owned = self.sql(expression, "owned")
1209        owned = f" OWNED BY {owned}" if owned else ""
1210
1211        cache = expression.args.get("cache")
1212        if cache is None:
1213            cache_str = ""
1214        elif cache is True:
1215            cache_str = " CACHE"
1216        else:
1217            cache_str = f" CACHE {cache}"
1218
1219        options = self.expressions(expression, key="options", flat=True, sep=" ")
1220        options = f" {options}" if options else ""
1221
1222        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1223
1224    def clone_sql(self, expression: exp.Clone) -> str:
1225        this = self.sql(expression, "this")
1226        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1227        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1228        return f"{shallow}{keyword} {this}"
1229
1230    def describe_sql(self, expression: exp.Describe) -> str:
1231        style = expression.args.get("style")
1232        style = f" {style}" if style else ""
1233        partition = self.sql(expression, "partition")
1234        partition = f" {partition}" if partition else ""
1235        format = self.sql(expression, "format")
1236        format = f" {format}" if format else ""
1237
1238        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
1239
1240    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1241        tag = self.sql(expression, "tag")
1242        return f"${tag}${self.sql(expression, 'this')}${tag}$"
1243
1244    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1245        with_ = self.sql(expression, "with")
1246        if with_:
1247            sql = f"{with_}{self.sep()}{sql}"
1248        return sql
1249
1250    def with_sql(self, expression: exp.With) -> str:
1251        sql = self.expressions(expression, flat=True)
1252        recursive = (
1253            "RECURSIVE "
1254            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1255            else ""
1256        )
1257        search = self.sql(expression, "search")
1258        search = f" {search}" if search else ""
1259
1260        return f"WITH {recursive}{sql}{search}"
1261
1262    def cte_sql(self, expression: exp.CTE) -> str:
1263        alias = expression.args.get("alias")
1264        if alias:
1265            alias.add_comments(expression.pop_comments())
1266
1267        alias_sql = self.sql(expression, "alias")
1268
1269        materialized = expression.args.get("materialized")
1270        if materialized is False:
1271            materialized = "NOT MATERIALIZED "
1272        elif materialized:
1273            materialized = "MATERIALIZED "
1274
1275        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
1276
1277    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1278        alias = self.sql(expression, "this")
1279        columns = self.expressions(expression, key="columns", flat=True)
1280        columns = f"({columns})" if columns else ""
1281
1282        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1283            columns = ""
1284            self.unsupported("Named columns are not supported in table alias.")
1285
1286        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1287            alias = self._next_name()
1288
1289        return f"{alias}{columns}"
1290
1291    def bitstring_sql(self, expression: exp.BitString) -> str:
1292        this = self.sql(expression, "this")
1293        if self.dialect.BIT_START:
1294            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1295        return f"{int(this, 2)}"
1296
1297    def hexstring_sql(
1298        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
1299    ) -> str:
1300        this = self.sql(expression, "this")
1301        is_integer_type = expression.args.get("is_integer")
1302
1303        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
1304            not self.dialect.HEX_START and not binary_function_repr
1305        ):
1306            # Integer representation will be returned if:
1307            # - The read dialect treats the hex value as integer literal but not the write
1308            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
1309            return f"{int(this, 16)}"
1310
1311        if not is_integer_type:
1312            # Read dialect treats the hex value as BINARY/BLOB
1313            if binary_function_repr:
1314                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
1315                return self.func(binary_function_repr, exp.Literal.string(this))
1316            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
1317                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
1318                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
1319
1320        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1321
1322    def bytestring_sql(self, expression: exp.ByteString) -> str:
1323        this = self.sql(expression, "this")
1324        if self.dialect.BYTE_START:
1325            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1326        return this
1327
1328    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1329        this = self.sql(expression, "this")
1330        escape = expression.args.get("escape")
1331
1332        if self.dialect.UNICODE_START:
1333            escape_substitute = r"\\\1"
1334            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
1335        else:
1336            escape_substitute = r"\\u\1"
1337            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
1338
1339        if escape:
1340            escape_pattern = re.compile(rf"{escape.name}(\d+)")
1341            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
1342        else:
1343            escape_pattern = ESCAPED_UNICODE_RE
1344            escape_sql = ""
1345
1346        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1347            this = escape_pattern.sub(escape_substitute, this)
1348
1349        return f"{left_quote}{this}{right_quote}{escape_sql}"
1350
1351    def rawstring_sql(self, expression: exp.RawString) -> str:
1352        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
1353        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1354
1355    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1356        this = self.sql(expression, "this")
1357        specifier = self.sql(expression, "expression")
1358        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1359        return f"{this}{specifier}"
1360
1361    def datatype_sql(self, expression: exp.DataType) -> str:
1362        nested = ""
1363        values = ""
1364        interior = self.expressions(expression, flat=True)
1365
1366        type_value = expression.this
1367        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1368            type_sql = self.sql(expression, "kind")
1369        else:
1370            type_sql = (
1371                self.TYPE_MAPPING.get(type_value, type_value.value)
1372                if isinstance(type_value, exp.DataType.Type)
1373                else type_value
1374            )
1375
1376        if interior:
1377            if expression.args.get("nested"):
1378                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1379                if expression.args.get("values") is not None:
1380                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1381                    values = self.expressions(expression, key="values", flat=True)
1382                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1383            elif type_value == exp.DataType.Type.INTERVAL:
1384                nested = f" {interior}"
1385            else:
1386                nested = f"({interior})"
1387
1388        type_sql = f"{type_sql}{nested}{values}"
1389        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1390            exp.DataType.Type.TIMETZ,
1391            exp.DataType.Type.TIMESTAMPTZ,
1392        ):
1393            type_sql = f"{type_sql} WITH TIME ZONE"
1394
1395        return type_sql
1396
1397    def directory_sql(self, expression: exp.Directory) -> str:
1398        local = "LOCAL " if expression.args.get("local") else ""
1399        row_format = self.sql(expression, "row_format")
1400        row_format = f" {row_format}" if row_format else ""
1401        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1402
1403    def delete_sql(self, expression: exp.Delete) -> str:
1404        this = self.sql(expression, "this")
1405        this = f" FROM {this}" if this else ""
1406        using = self.sql(expression, "using")
1407        using = f" USING {using}" if using else ""
1408        cluster = self.sql(expression, "cluster")
1409        cluster = f" {cluster}" if cluster else ""
1410        where = self.sql(expression, "where")
1411        returning = self.sql(expression, "returning")
1412        limit = self.sql(expression, "limit")
1413        tables = self.expressions(expression, key="tables")
1414        tables = f" {tables}" if tables else ""
1415        if self.RETURNING_END:
1416            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
1417        else:
1418            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
1419        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1420
1421    def drop_sql(self, expression: exp.Drop) -> str:
1422        this = self.sql(expression, "this")
1423        expressions = self.expressions(expression, flat=True)
1424        expressions = f" ({expressions})" if expressions else ""
1425        kind = expression.args["kind"]
1426        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1427        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1428        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1429        on_cluster = self.sql(expression, "cluster")
1430        on_cluster = f" {on_cluster}" if on_cluster else ""
1431        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1432        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1433        cascade = " CASCADE" if expression.args.get("cascade") else ""
1434        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1435        purge = " PURGE" if expression.args.get("purge") else ""
1436        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
1437
1438    def set_operation(self, expression: exp.SetOperation) -> str:
1439        op_type = type(expression)
1440        op_name = op_type.key.upper()
1441
1442        distinct = expression.args.get("distinct")
1443        if (
1444            distinct is False
1445            and op_type in (exp.Except, exp.Intersect)
1446            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
1447        ):
1448            self.unsupported(f"{op_name} ALL is not supported")
1449
1450        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
1451
1452        if distinct is None:
1453            distinct = default_distinct
1454            if distinct is None:
1455                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
1456
1457        if distinct is default_distinct:
1458            distinct_or_all = ""
1459        else:
1460            distinct_or_all = " DISTINCT" if distinct else " ALL"
1461
1462        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
1463        side_kind = f"{side_kind} " if side_kind else ""
1464
1465        by_name = " BY NAME" if expression.args.get("by_name") else ""
1466        on = self.expressions(expression, key="on", flat=True)
1467        on = f" ON ({on})" if on else ""
1468
1469        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
1470
1471    def set_operations(self, expression: exp.SetOperation) -> str:
1472        if not self.SET_OP_MODIFIERS:
1473            limit = expression.args.get("limit")
1474            order = expression.args.get("order")
1475
1476            if limit or order:
1477                select = self._move_ctes_to_top_level(
1478                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1479                )
1480
1481                if limit:
1482                    select = select.limit(limit.pop(), copy=False)
1483                if order:
1484                    select = select.order_by(order.pop(), copy=False)
1485                return self.sql(select)
1486
1487        sqls: t.List[str] = []
1488        stack: t.List[t.Union[str, exp.Expression]] = [expression]
1489
1490        while stack:
1491            node = stack.pop()
1492
1493            if isinstance(node, exp.SetOperation):
1494                stack.append(node.expression)
1495                stack.append(
1496                    self.maybe_comment(
1497                        self.set_operation(node), comments=node.comments, separated=True
1498                    )
1499                )
1500                stack.append(node.this)
1501            else:
1502                sqls.append(self.sql(node))
1503
1504        this = self.sep().join(sqls)
1505        this = self.query_modifiers(expression, this)
1506        return self.prepend_ctes(expression, this)
1507
1508    def fetch_sql(self, expression: exp.Fetch) -> str:
1509        direction = expression.args.get("direction")
1510        direction = f" {direction}" if direction else ""
1511        count = self.sql(expression, "count")
1512        count = f" {count}" if count else ""
1513        limit_options = self.sql(expression, "limit_options")
1514        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
1515        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
1516
1517    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
1518        percent = " PERCENT" if expression.args.get("percent") else ""
1519        rows = " ROWS" if expression.args.get("rows") else ""
1520        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
1521        if not with_ties and rows:
1522            with_ties = " ONLY"
1523        return f"{percent}{rows}{with_ties}"
1524
1525    def filter_sql(self, expression: exp.Filter) -> str:
1526        if self.AGGREGATE_FILTER_SUPPORTED:
1527            this = self.sql(expression, "this")
1528            where = self.sql(expression, "expression").strip()
1529            return f"{this} FILTER({where})"
1530
1531        agg = expression.this
1532        agg_arg = agg.this
1533        cond = expression.expression.this
1534        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1535        return self.sql(agg)
1536
1537    def hint_sql(self, expression: exp.Hint) -> str:
1538        if not self.QUERY_HINTS:
1539            self.unsupported("Hints are not supported")
1540            return ""
1541
1542        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1543
1544    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
1545        using = self.sql(expression, "using")
1546        using = f" USING {using}" if using else ""
1547        columns = self.expressions(expression, key="columns", flat=True)
1548        columns = f"({columns})" if columns else ""
1549        partition_by = self.expressions(expression, key="partition_by", flat=True)
1550        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1551        where = self.sql(expression, "where")
1552        include = self.expressions(expression, key="include", flat=True)
1553        if include:
1554            include = f" INCLUDE ({include})"
1555        with_storage = self.expressions(expression, key="with_storage", flat=True)
1556        with_storage = f" WITH ({with_storage})" if with_storage else ""
1557        tablespace = self.sql(expression, "tablespace")
1558        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
1559        on = self.sql(expression, "on")
1560        on = f" ON {on}" if on else ""
1561
1562        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
1563
1564    def index_sql(self, expression: exp.Index) -> str:
1565        unique = "UNIQUE " if expression.args.get("unique") else ""
1566        primary = "PRIMARY " if expression.args.get("primary") else ""
1567        amp = "AMP " if expression.args.get("amp") else ""
1568        name = self.sql(expression, "this")
1569        name = f"{name} " if name else ""
1570        table = self.sql(expression, "table")
1571        table = f"{self.INDEX_ON} {table}" if table else ""
1572
1573        index = "INDEX " if not table else ""
1574
1575        params = self.sql(expression, "params")
1576        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1577
1578    def identifier_sql(self, expression: exp.Identifier) -> str:
1579        text = expression.name
1580        lower = text.lower()
1581        text = lower if self.normalize and not expression.quoted else text
1582        text = text.replace(self._identifier_end, self._escaped_identifier_end)
1583        if (
1584            expression.quoted
1585            or self.dialect.can_identify(text, self.identify)
1586            or lower in self.RESERVED_KEYWORDS
1587            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1588        ):
1589            text = f"{self._identifier_start}{text}{self._identifier_end}"
1590        return text
1591
1592    def hex_sql(self, expression: exp.Hex) -> str:
1593        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1594        if self.dialect.HEX_LOWERCASE:
1595            text = self.func("LOWER", text)
1596
1597        return text
1598
1599    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
1600        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1601        if not self.dialect.HEX_LOWERCASE:
1602            text = self.func("LOWER", text)
1603        return text
1604
1605    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1606        input_format = self.sql(expression, "input_format")
1607        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1608        output_format = self.sql(expression, "output_format")
1609        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1610        return self.sep().join((input_format, output_format))
1611
1612    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1613        string = self.sql(exp.Literal.string(expression.name))
1614        return f"{prefix}{string}"
1615
1616    def partition_sql(self, expression: exp.Partition) -> str:
1617        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
1618        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
1619
1620    def properties_sql(self, expression: exp.Properties) -> str:
1621        root_properties = []
1622        with_properties = []
1623
1624        for p in expression.expressions:
1625            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1626            if p_loc == exp.Properties.Location.POST_WITH:
1627                with_properties.append(p)
1628            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1629                root_properties.append(p)
1630
1631        root_props = self.root_properties(exp.Properties(expressions=root_properties))
1632        with_props = self.with_properties(exp.Properties(expressions=with_properties))
1633
1634        if root_props and with_props and not self.pretty:
1635            with_props = " " + with_props
1636
1637        return root_props + with_props
1638
1639    def root_properties(self, properties: exp.Properties) -> str:
1640        if properties.expressions:
1641            return self.expressions(properties, indent=False, sep=" ")
1642        return ""
1643
1644    def properties(
1645        self,
1646        properties: exp.Properties,
1647        prefix: str = "",
1648        sep: str = ", ",
1649        suffix: str = "",
1650        wrapped: bool = True,
1651    ) -> str:
1652        if properties.expressions:
1653            expressions = self.expressions(properties, sep=sep, indent=False)
1654            if expressions:
1655                expressions = self.wrap(expressions) if wrapped else expressions
1656                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1657        return ""
1658
1659    def with_properties(self, properties: exp.Properties) -> str:
1660        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
1661
1662    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1663        properties_locs = defaultdict(list)
1664        for p in properties.expressions:
1665            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1666            if p_loc != exp.Properties.Location.UNSUPPORTED:
1667                properties_locs[p_loc].append(p)
1668            else:
1669                self.unsupported(f"Unsupported property {p.key}")
1670
1671        return properties_locs
1672
1673    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1674        if isinstance(expression.this, exp.Dot):
1675            return self.sql(expression, "this")
1676        return f"'{expression.name}'" if string_key else expression.name
1677
1678    def property_sql(self, expression: exp.Property) -> str:
1679        property_cls = expression.__class__
1680        if property_cls == exp.Property:
1681            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1682
1683        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1684        if not property_name:
1685            self.unsupported(f"Unsupported property {expression.key}")
1686
1687        return f"{property_name}={self.sql(expression, 'this')}"
1688
1689    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1690        if self.SUPPORTS_CREATE_TABLE_LIKE:
1691            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1692            options = f" {options}" if options else ""
1693
1694            like = f"LIKE {self.sql(expression, 'this')}{options}"
1695            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1696                like = f"({like})"
1697
1698            return like
1699
1700        if expression.expressions:
1701            self.unsupported("Transpilation of LIKE property options is unsupported")
1702
1703        select = exp.select("*").from_(expression.this).limit(0)
1704        return f"AS {self.sql(select)}"
1705
1706    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1707        no = "NO " if expression.args.get("no") else ""
1708        protection = " PROTECTION" if expression.args.get("protection") else ""
1709        return f"{no}FALLBACK{protection}"
1710
1711    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1712        no = "NO " if expression.args.get("no") else ""
1713        local = expression.args.get("local")
1714        local = f"{local} " if local else ""
1715        dual = "DUAL " if expression.args.get("dual") else ""
1716        before = "BEFORE " if expression.args.get("before") else ""
1717        after = "AFTER " if expression.args.get("after") else ""
1718        return f"{no}{local}{dual}{before}{after}JOURNAL"
1719
1720    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1721        freespace = self.sql(expression, "this")
1722        percent = " PERCENT" if expression.args.get("percent") else ""
1723        return f"FREESPACE={freespace}{percent}"
1724
1725    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1726        if expression.args.get("default"):
1727            property = "DEFAULT"
1728        elif expression.args.get("on"):
1729            property = "ON"
1730        else:
1731            property = "OFF"
1732        return f"CHECKSUM={property}"
1733
1734    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1735        if expression.args.get("no"):
1736            return "NO MERGEBLOCKRATIO"
1737        if expression.args.get("default"):
1738            return "DEFAULT MERGEBLOCKRATIO"
1739
1740        percent = " PERCENT" if expression.args.get("percent") else ""
1741        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1742
1743    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1744        default = expression.args.get("default")
1745        minimum = expression.args.get("minimum")
1746        maximum = expression.args.get("maximum")
1747        if default or minimum or maximum:
1748            if default:
1749                prop = "DEFAULT"
1750            elif minimum:
1751                prop = "MINIMUM"
1752            else:
1753                prop = "MAXIMUM"
1754            return f"{prop} DATABLOCKSIZE"
1755        units = expression.args.get("units")
1756        units = f" {units}" if units else ""
1757        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1758
1759    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1760        autotemp = expression.args.get("autotemp")
1761        always = expression.args.get("always")
1762        default = expression.args.get("default")
1763        manual = expression.args.get("manual")
1764        never = expression.args.get("never")
1765
1766        if autotemp is not None:
1767            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1768        elif always:
1769            prop = "ALWAYS"
1770        elif default:
1771            prop = "DEFAULT"
1772        elif manual:
1773            prop = "MANUAL"
1774        elif never:
1775            prop = "NEVER"
1776        return f"BLOCKCOMPRESSION={prop}"
1777
1778    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1779        no = expression.args.get("no")
1780        no = " NO" if no else ""
1781        concurrent = expression.args.get("concurrent")
1782        concurrent = " CONCURRENT" if concurrent else ""
1783        target = self.sql(expression, "target")
1784        target = f" {target}" if target else ""
1785        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1786
1787    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1788        if isinstance(expression.this, list):
1789            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1790        if expression.this:
1791            modulus = self.sql(expression, "this")
1792            remainder = self.sql(expression, "expression")
1793            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1794
1795        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1796        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1797        return f"FROM ({from_expressions}) TO ({to_expressions})"
1798
1799    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1800        this = self.sql(expression, "this")
1801
1802        for_values_or_default = expression.expression
1803        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1804            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1805        else:
1806            for_values_or_default = " DEFAULT"
1807
1808        return f"PARTITION OF {this}{for_values_or_default}"
1809
1810    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1811        kind = expression.args.get("kind")
1812        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1813        for_or_in = expression.args.get("for_or_in")
1814        for_or_in = f" {for_or_in}" if for_or_in else ""
1815        lock_type = expression.args.get("lock_type")
1816        override = " OVERRIDE" if expression.args.get("override") else ""
1817        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1818
1819    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1820        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1821        statistics = expression.args.get("statistics")
1822        statistics_sql = ""
1823        if statistics is not None:
1824            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1825        return f"{data_sql}{statistics_sql}"
1826
1827    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1828        this = self.sql(expression, "this")
1829        this = f"HISTORY_TABLE={this}" if this else ""
1830        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
1831        data_consistency = (
1832            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
1833        )
1834        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
1835        retention_period = (
1836            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
1837        )
1838
1839        if this:
1840            on_sql = self.func("ON", this, data_consistency, retention_period)
1841        else:
1842            on_sql = "ON" if expression.args.get("on") else "OFF"
1843
1844        sql = f"SYSTEM_VERSIONING={on_sql}"
1845
1846        return f"WITH({sql})" if expression.args.get("with") else sql
1847
1848    def insert_sql(self, expression: exp.Insert) -> str:
1849        hint = self.sql(expression, "hint")
1850        overwrite = expression.args.get("overwrite")
1851
1852        if isinstance(expression.this, exp.Directory):
1853            this = " OVERWRITE" if overwrite else " INTO"
1854        else:
1855            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1856
1857        stored = self.sql(expression, "stored")
1858        stored = f" {stored}" if stored else ""
1859        alternative = expression.args.get("alternative")
1860        alternative = f" OR {alternative}" if alternative else ""
1861        ignore = " IGNORE" if expression.args.get("ignore") else ""
1862        is_function = expression.args.get("is_function")
1863        if is_function:
1864            this = f"{this} FUNCTION"
1865        this = f"{this} {self.sql(expression, 'this')}"
1866
1867        exists = " IF EXISTS" if expression.args.get("exists") else ""
1868        where = self.sql(expression, "where")
1869        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1870        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1871        on_conflict = self.sql(expression, "conflict")
1872        on_conflict = f" {on_conflict}" if on_conflict else ""
1873        by_name = " BY NAME" if expression.args.get("by_name") else ""
1874        returning = self.sql(expression, "returning")
1875
1876        if self.RETURNING_END:
1877            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1878        else:
1879            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1880
1881        partition_by = self.sql(expression, "partition")
1882        partition_by = f" {partition_by}" if partition_by else ""
1883        settings = self.sql(expression, "settings")
1884        settings = f" {settings}" if settings else ""
1885
1886        source = self.sql(expression, "source")
1887        source = f"TABLE {source}" if source else ""
1888
1889        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
1890        return self.prepend_ctes(expression, sql)
1891
1892    def introducer_sql(self, expression: exp.Introducer) -> str:
1893        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1894
1895    def kill_sql(self, expression: exp.Kill) -> str:
1896        kind = self.sql(expression, "kind")
1897        kind = f" {kind}" if kind else ""
1898        this = self.sql(expression, "this")
1899        this = f" {this}" if this else ""
1900        return f"KILL{kind}{this}"
1901
1902    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1903        return expression.name
1904
1905    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1906        return expression.name
1907
1908    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1909        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1910
1911        constraint = self.sql(expression, "constraint")
1912        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1913
1914        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1915        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1916        action = self.sql(expression, "action")
1917
1918        expressions = self.expressions(expression, flat=True)
1919        if expressions:
1920            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1921            expressions = f" {set_keyword}{expressions}"
1922
1923        where = self.sql(expression, "where")
1924        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
1925
1926    def returning_sql(self, expression: exp.Returning) -> str:
1927        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1928
1929    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1930        fields = self.sql(expression, "fields")
1931        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1932        escaped = self.sql(expression, "escaped")
1933        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1934        items = self.sql(expression, "collection_items")
1935        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1936        keys = self.sql(expression, "map_keys")
1937        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1938        lines = self.sql(expression, "lines")
1939        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1940        null = self.sql(expression, "null")
1941        null = f" NULL DEFINED AS {null}" if null else ""
1942        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1943
1944    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1945        return f"WITH ({self.expressions(expression, flat=True)})"
1946
1947    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1948        this = f"{self.sql(expression, 'this')} INDEX"
1949        target = self.sql(expression, "target")
1950        target = f" FOR {target}" if target else ""
1951        return f"{this}{target} ({self.expressions(expression, flat=True)})"
1952
1953    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1954        this = self.sql(expression, "this")
1955        kind = self.sql(expression, "kind")
1956        expr = self.sql(expression, "expression")
1957        return f"{this} ({kind} => {expr})"
1958
1959    def table_parts(self, expression: exp.Table) -> str:
1960        return ".".join(
1961            self.sql(part)
1962            for part in (
1963                expression.args.get("catalog"),
1964                expression.args.get("db"),
1965                expression.args.get("this"),
1966            )
1967            if part is not None
1968        )
1969
1970    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1971        table = self.table_parts(expression)
1972        only = "ONLY " if expression.args.get("only") else ""
1973        partition = self.sql(expression, "partition")
1974        partition = f" {partition}" if partition else ""
1975        version = self.sql(expression, "version")
1976        version = f" {version}" if version else ""
1977        alias = self.sql(expression, "alias")
1978        alias = f"{sep}{alias}" if alias else ""
1979
1980        sample = self.sql(expression, "sample")
1981        if self.dialect.ALIAS_POST_TABLESAMPLE:
1982            sample_pre_alias = sample
1983            sample_post_alias = ""
1984        else:
1985            sample_pre_alias = ""
1986            sample_post_alias = sample
1987
1988        hints = self.expressions(expression, key="hints", sep=" ")
1989        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1990        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
1991        joins = self.indent(
1992            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
1993        )
1994        laterals = self.expressions(expression, key="laterals", sep="")
1995
1996        file_format = self.sql(expression, "format")
1997        if file_format:
1998            pattern = self.sql(expression, "pattern")
1999            pattern = f", PATTERN => {pattern}" if pattern else ""
2000            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2001
2002        ordinality = expression.args.get("ordinality") or ""
2003        if ordinality:
2004            ordinality = f" WITH ORDINALITY{alias}"
2005            alias = ""
2006
2007        when = self.sql(expression, "when")
2008        if when:
2009            table = f"{table} {when}"
2010
2011        changes = self.sql(expression, "changes")
2012        changes = f" {changes}" if changes else ""
2013
2014        rows_from = self.expressions(expression, key="rows_from")
2015        if rows_from:
2016            table = f"ROWS FROM {self.wrap(rows_from)}"
2017
2018        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
2019
2020    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
2021        table = self.func("TABLE", expression.this)
2022        alias = self.sql(expression, "alias")
2023        alias = f" AS {alias}" if alias else ""
2024        sample = self.sql(expression, "sample")
2025        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2026        joins = self.indent(
2027            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2028        )
2029        return f"{table}{alias}{pivots}{sample}{joins}"
2030
2031    def tablesample_sql(
2032        self,
2033        expression: exp.TableSample,
2034        tablesample_keyword: t.Optional[str] = None,
2035    ) -> str:
2036        method = self.sql(expression, "method")
2037        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
2038        numerator = self.sql(expression, "bucket_numerator")
2039        denominator = self.sql(expression, "bucket_denominator")
2040        field = self.sql(expression, "bucket_field")
2041        field = f" ON {field}" if field else ""
2042        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
2043        seed = self.sql(expression, "seed")
2044        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
2045
2046        size = self.sql(expression, "size")
2047        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
2048            size = f"{size} ROWS"
2049
2050        percent = self.sql(expression, "percent")
2051        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
2052            percent = f"{percent} PERCENT"
2053
2054        expr = f"{bucket}{percent}{size}"
2055        if self.TABLESAMPLE_REQUIRES_PARENS:
2056            expr = f"({expr})"
2057
2058        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
2059
2060    def pivot_sql(self, expression: exp.Pivot) -> str:
2061        expressions = self.expressions(expression, flat=True)
2062        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
2063
2064        group = self.sql(expression, "group")
2065
2066        if expression.this:
2067            this = self.sql(expression, "this")
2068            if not expressions:
2069                return f"UNPIVOT {this}"
2070
2071            on = f"{self.seg('ON')} {expressions}"
2072            into = self.sql(expression, "into")
2073            into = f"{self.seg('INTO')} {into}" if into else ""
2074            using = self.expressions(expression, key="using", flat=True)
2075            using = f"{self.seg('USING')} {using}" if using else ""
2076            return f"{direction} {this}{on}{into}{using}{group}"
2077
2078        alias = self.sql(expression, "alias")
2079        alias = f" AS {alias}" if alias else ""
2080
2081        fields = self.expressions(
2082            expression,
2083            "fields",
2084            sep=" ",
2085            dynamic=True,
2086            new_line=True,
2087            skip_first=True,
2088            skip_last=True,
2089        )
2090
2091        include_nulls = expression.args.get("include_nulls")
2092        if include_nulls is not None:
2093            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
2094        else:
2095            nulls = ""
2096
2097        default_on_null = self.sql(expression, "default_on_null")
2098        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
2099        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
2100
2101    def version_sql(self, expression: exp.Version) -> str:
2102        this = f"FOR {expression.name}"
2103        kind = expression.text("kind")
2104        expr = self.sql(expression, "expression")
2105        return f"{this} {kind} {expr}"
2106
2107    def tuple_sql(self, expression: exp.Tuple) -> str:
2108        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
2109
2110    def update_sql(self, expression: exp.Update) -> str:
2111        this = self.sql(expression, "this")
2112        set_sql = self.expressions(expression, flat=True)
2113        from_sql = self.sql(expression, "from")
2114        where_sql = self.sql(expression, "where")
2115        returning = self.sql(expression, "returning")
2116        order = self.sql(expression, "order")
2117        limit = self.sql(expression, "limit")
2118        if self.RETURNING_END:
2119            expression_sql = f"{from_sql}{where_sql}{returning}"
2120        else:
2121            expression_sql = f"{returning}{from_sql}{where_sql}"
2122        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
2123        return self.prepend_ctes(expression, sql)
2124
2125    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
2126        values_as_table = values_as_table and self.VALUES_AS_TABLE
2127
2128        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
2129        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
2130            args = self.expressions(expression)
2131            alias = self.sql(expression, "alias")
2132            values = f"VALUES{self.seg('')}{args}"
2133            values = (
2134                f"({values})"
2135                if self.WRAP_DERIVED_VALUES
2136                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
2137                else values
2138            )
2139            return f"{values} AS {alias}" if alias else values
2140
2141        # Converts `VALUES...` expression into a series of select unions.
2142        alias_node = expression.args.get("alias")
2143        column_names = alias_node and alias_node.columns
2144
2145        selects: t.List[exp.Query] = []
2146
2147        for i, tup in enumerate(expression.expressions):
2148            row = tup.expressions
2149
2150            if i == 0 and column_names:
2151                row = [
2152                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
2153                ]
2154
2155            selects.append(exp.Select(expressions=row))
2156
2157        if self.pretty:
2158            # This may result in poor performance for large-cardinality `VALUES` tables, due to
2159            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
2160            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
2161            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
2162            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
2163
2164        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
2165        unions = " UNION ALL ".join(self.sql(select) for select in selects)
2166        return f"({unions}){alias}"
2167
2168    def var_sql(self, expression: exp.Var) -> str:
2169        return self.sql(expression, "this")
2170
2171    @unsupported_args("expressions")
2172    def into_sql(self, expression: exp.Into) -> str:
2173        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
2174        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
2175        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
2176
2177    def from_sql(self, expression: exp.From) -> str:
2178        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
2179
2180    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
2181        grouping_sets = self.expressions(expression, indent=False)
2182        return f"GROUPING SETS {self.wrap(grouping_sets)}"
2183
2184    def rollup_sql(self, expression: exp.Rollup) -> str:
2185        expressions = self.expressions(expression, indent=False)
2186        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
2187
2188    def cube_sql(self, expression: exp.Cube) -> str:
2189        expressions = self.expressions(expression, indent=False)
2190        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
2191
2192    def group_sql(self, expression: exp.Group) -> str:
2193        group_by_all = expression.args.get("all")
2194        if group_by_all is True:
2195            modifier = " ALL"
2196        elif group_by_all is False:
2197            modifier = " DISTINCT"
2198        else:
2199            modifier = ""
2200
2201        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
2202
2203        grouping_sets = self.expressions(expression, key="grouping_sets")
2204        cube = self.expressions(expression, key="cube")
2205        rollup = self.expressions(expression, key="rollup")
2206
2207        groupings = csv(
2208            self.seg(grouping_sets) if grouping_sets else "",
2209            self.seg(cube) if cube else "",
2210            self.seg(rollup) if rollup else "",
2211            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
2212            sep=self.GROUPINGS_SEP,
2213        )
2214
2215        if (
2216            expression.expressions
2217            and groupings
2218            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2219        ):
2220            group_by = f"{group_by}{self.GROUPINGS_SEP}"
2221
2222        return f"{group_by}{groupings}"
2223
2224    def having_sql(self, expression: exp.Having) -> str:
2225        this = self.indent(self.sql(expression, "this"))
2226        return f"{self.seg('HAVING')}{self.sep()}{this}"
2227
2228    def connect_sql(self, expression: exp.Connect) -> str:
2229        start = self.sql(expression, "start")
2230        start = self.seg(f"START WITH {start}") if start else ""
2231        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
2232        connect = self.sql(expression, "connect")
2233        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
2234        return start + connect
2235
2236    def prior_sql(self, expression: exp.Prior) -> str:
2237        return f"PRIOR {self.sql(expression, 'this')}"
2238
2239    def join_sql(self, expression: exp.Join) -> str:
2240        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2241            side = None
2242        else:
2243            side = expression.side
2244
2245        op_sql = " ".join(
2246            op
2247            for op in (
2248                expression.method,
2249                "GLOBAL" if expression.args.get("global") else None,
2250                side,
2251                expression.kind,
2252                expression.hint if self.JOIN_HINTS else None,
2253            )
2254            if op
2255        )
2256        match_cond = self.sql(expression, "match_condition")
2257        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2258        on_sql = self.sql(expression, "on")
2259        using = expression.args.get("using")
2260
2261        if not on_sql and using:
2262            on_sql = csv(*(self.sql(column) for column in using))
2263
2264        this = expression.this
2265        this_sql = self.sql(this)
2266
2267        exprs = self.expressions(expression)
2268        if exprs:
2269            this_sql = f"{this_sql},{self.seg(exprs)}"
2270
2271        if on_sql:
2272            on_sql = self.indent(on_sql, skip_first=True)
2273            space = self.seg(" " * self.pad) if self.pretty else " "
2274            if using:
2275                on_sql = f"{space}USING ({on_sql})"
2276            else:
2277                on_sql = f"{space}ON {on_sql}"
2278        elif not op_sql:
2279            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2280                return f" {this_sql}"
2281
2282            return f", {this_sql}"
2283
2284        if op_sql != "STRAIGHT_JOIN":
2285            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2286
2287        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
2288
2289    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
2290        args = self.expressions(expression, flat=True)
2291        args = f"({args})" if len(args.split(",")) > 1 else args
2292        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
2293
2294    def lateral_op(self, expression: exp.Lateral) -> str:
2295        cross_apply = expression.args.get("cross_apply")
2296
2297        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
2298        if cross_apply is True:
2299            op = "INNER JOIN "
2300        elif cross_apply is False:
2301            op = "LEFT JOIN "
2302        else:
2303            op = ""
2304
2305        return f"{op}LATERAL"
2306
2307    def lateral_sql(self, expression: exp.Lateral) -> str:
2308        this = self.sql(expression, "this")
2309
2310        if expression.args.get("view"):
2311            alias = expression.args["alias"]
2312            columns = self.expressions(alias, key="columns", flat=True)
2313            table = f" {alias.name}" if alias.name else ""
2314            columns = f" AS {columns}" if columns else ""
2315            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
2316            return f"{op_sql}{self.sep()}{this}{table}{columns}"
2317
2318        alias = self.sql(expression, "alias")
2319        alias = f" AS {alias}" if alias else ""
2320
2321        ordinality = expression.args.get("ordinality") or ""
2322        if ordinality:
2323            ordinality = f" WITH ORDINALITY{alias}"
2324            alias = ""
2325
2326        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
2327
2328    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2329        this = self.sql(expression, "this")
2330
2331        args = [
2332            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2333            for e in (expression.args.get(k) for k in ("offset", "expression"))
2334            if e
2335        ]
2336
2337        args_sql = ", ".join(self.sql(e) for e in args)
2338        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
2339        expressions = self.expressions(expression, flat=True)
2340        limit_options = self.sql(expression, "limit_options")
2341        expressions = f" BY {expressions}" if expressions else ""
2342
2343        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
2344
2345    def offset_sql(self, expression: exp.Offset) -> str:
2346        this = self.sql(expression, "this")
2347        value = expression.expression
2348        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
2349        expressions = self.expressions(expression, flat=True)
2350        expressions = f" BY {expressions}" if expressions else ""
2351        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
2352
2353    def setitem_sql(self, expression: exp.SetItem) -> str:
2354        kind = self.sql(expression, "kind")
2355        kind = f"{kind} " if kind else ""
2356        this = self.sql(expression, "this")
2357        expressions = self.expressions(expression)
2358        collate = self.sql(expression, "collate")
2359        collate = f" COLLATE {collate}" if collate else ""
2360        global_ = "GLOBAL " if expression.args.get("global") else ""
2361        return f"{global_}{kind}{this}{expressions}{collate}"
2362
2363    def set_sql(self, expression: exp.Set) -> str:
2364        expressions = f" {self.expressions(expression, flat=True)}"
2365        tag = " TAG" if expression.args.get("tag") else ""
2366        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2367
2368    def pragma_sql(self, expression: exp.Pragma) -> str:
2369        return f"PRAGMA {self.sql(expression, 'this')}"
2370
2371    def lock_sql(self, expression: exp.Lock) -> str:
2372        if not self.LOCKING_READS_SUPPORTED:
2373            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
2374            return ""
2375
2376        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
2377        expressions = self.expressions(expression, flat=True)
2378        expressions = f" OF {expressions}" if expressions else ""
2379        wait = expression.args.get("wait")
2380
2381        if wait is not None:
2382            if isinstance(wait, exp.Literal):
2383                wait = f" WAIT {self.sql(wait)}"
2384            else:
2385                wait = " NOWAIT" if wait else " SKIP LOCKED"
2386
2387        return f"{lock_type}{expressions}{wait or ''}"
2388
2389    def literal_sql(self, expression: exp.Literal) -> str:
2390        text = expression.this or ""
2391        if expression.is_string:
2392            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
2393        return text
2394
2395    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2396        if self.dialect.ESCAPED_SEQUENCES:
2397            to_escaped = self.dialect.ESCAPED_SEQUENCES
2398            text = "".join(
2399                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
2400            )
2401
2402        return self._replace_line_breaks(text).replace(
2403            self.dialect.QUOTE_END, self._escaped_quote_end
2404        )
2405
2406    def loaddata_sql(self, expression: exp.LoadData) -> str:
2407        local = " LOCAL" if expression.args.get("local") else ""
2408        inpath = f" INPATH {self.sql(expression, 'inpath')}"
2409        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
2410        this = f" INTO TABLE {self.sql(expression, 'this')}"
2411        partition = self.sql(expression, "partition")
2412        partition = f" {partition}" if partition else ""
2413        input_format = self.sql(expression, "input_format")
2414        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
2415        serde = self.sql(expression, "serde")
2416        serde = f" SERDE {serde}" if serde else ""
2417        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2418
2419    def null_sql(self, *_) -> str:
2420        return "NULL"
2421
2422    def boolean_sql(self, expression: exp.Boolean) -> str:
2423        return "TRUE" if expression.this else "FALSE"
2424
2425    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
2426        this = self.sql(expression, "this")
2427        this = f"{this} " if this else this
2428        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2429        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2430
2431    def withfill_sql(self, expression: exp.WithFill) -> str:
2432        from_sql = self.sql(expression, "from")
2433        from_sql = f" FROM {from_sql}" if from_sql else ""
2434        to_sql = self.sql(expression, "to")
2435        to_sql = f" TO {to_sql}" if to_sql else ""
2436        step_sql = self.sql(expression, "step")
2437        step_sql = f" STEP {step_sql}" if step_sql else ""
2438        interpolated_values = [
2439            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
2440            if isinstance(e, exp.Alias)
2441            else self.sql(e, "this")
2442            for e in expression.args.get("interpolate") or []
2443        ]
2444        interpolate = (
2445            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2446        )
2447        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
2448
2449    def cluster_sql(self, expression: exp.Cluster) -> str:
2450        return self.op_expressions("CLUSTER BY", expression)
2451
2452    def distribute_sql(self, expression: exp.Distribute) -> str:
2453        return self.op_expressions("DISTRIBUTE BY", expression)
2454
2455    def sort_sql(self, expression: exp.Sort) -> str:
2456        return self.op_expressions("SORT BY", expression)
2457
2458    def ordered_sql(self, expression: exp.Ordered) -> str:
2459        desc = expression.args.get("desc")
2460        asc = not desc
2461
2462        nulls_first = expression.args.get("nulls_first")
2463        nulls_last = not nulls_first
2464        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2465        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2466        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2467
2468        this = self.sql(expression, "this")
2469
2470        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2471        nulls_sort_change = ""
2472        if nulls_first and (
2473            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2474        ):
2475            nulls_sort_change = " NULLS FIRST"
2476        elif (
2477            nulls_last
2478            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2479            and not nulls_are_last
2480        ):
2481            nulls_sort_change = " NULLS LAST"
2482
2483        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2484        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2485            window = expression.find_ancestor(exp.Window, exp.Select)
2486            if isinstance(window, exp.Window) and window.args.get("spec"):
2487                self.unsupported(
2488                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2489                )
2490                nulls_sort_change = ""
2491            elif self.NULL_ORDERING_SUPPORTED is False and (
2492                (asc and nulls_sort_change == " NULLS LAST")
2493                or (desc and nulls_sort_change == " NULLS FIRST")
2494            ):
2495                # BigQuery does not allow these ordering/nulls combinations when used under
2496                # an aggregation func or under a window containing one
2497                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
2498
2499                if isinstance(ancestor, exp.Window):
2500                    ancestor = ancestor.this
2501                if isinstance(ancestor, exp.AggFunc):
2502                    self.unsupported(
2503                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
2504                    )
2505                    nulls_sort_change = ""
2506            elif self.NULL_ORDERING_SUPPORTED is None:
2507                if expression.this.is_int:
2508                    self.unsupported(
2509                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2510                    )
2511                elif not isinstance(expression.this, exp.Rand):
2512                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2513                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2514                nulls_sort_change = ""
2515
2516        with_fill = self.sql(expression, "with_fill")
2517        with_fill = f" {with_fill}" if with_fill else ""
2518
2519        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2520
2521    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
2522        window_frame = self.sql(expression, "window_frame")
2523        window_frame = f"{window_frame} " if window_frame else ""
2524
2525        this = self.sql(expression, "this")
2526
2527        return f"{window_frame}{this}"
2528
2529    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2530        partition = self.partition_by_sql(expression)
2531        order = self.sql(expression, "order")
2532        measures = self.expressions(expression, key="measures")
2533        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2534        rows = self.sql(expression, "rows")
2535        rows = self.seg(rows) if rows else ""
2536        after = self.sql(expression, "after")
2537        after = self.seg(after) if after else ""
2538        pattern = self.sql(expression, "pattern")
2539        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2540        definition_sqls = [
2541            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2542            for definition in expression.args.get("define", [])
2543        ]
2544        definitions = self.expressions(sqls=definition_sqls)
2545        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2546        body = "".join(
2547            (
2548                partition,
2549                order,
2550                measures,
2551                rows,
2552                after,
2553                pattern,
2554                define,
2555            )
2556        )
2557        alias = self.sql(expression, "alias")
2558        alias = f" {alias}" if alias else ""
2559        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2560
2561    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2562        limit = expression.args.get("limit")
2563
2564        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2565            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2566        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2567            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2568
2569        return csv(
2570            *sqls,
2571            *[self.sql(join) for join in expression.args.get("joins") or []],
2572            self.sql(expression, "match"),
2573            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2574            self.sql(expression, "prewhere"),
2575            self.sql(expression, "where"),
2576            self.sql(expression, "connect"),
2577            self.sql(expression, "group"),
2578            self.sql(expression, "having"),
2579            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2580            self.sql(expression, "order"),
2581            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
2582            *self.after_limit_modifiers(expression),
2583            self.options_modifier(expression),
2584            sep="",
2585        )
2586
2587    def options_modifier(self, expression: exp.Expression) -> str:
2588        options = self.expressions(expression, key="options")
2589        return f" {options}" if options else ""
2590
2591    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2592        self.unsupported("Unsupported query option.")
2593        return ""
2594
2595    def offset_limit_modifiers(
2596        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2597    ) -> t.List[str]:
2598        return [
2599            self.sql(expression, "offset") if fetch else self.sql(limit),
2600            self.sql(limit) if fetch else self.sql(expression, "offset"),
2601        ]
2602
2603    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2604        locks = self.expressions(expression, key="locks", sep=" ")
2605        locks = f" {locks}" if locks else ""
2606        return [locks, self.sql(expression, "sample")]
2607
2608    def select_sql(self, expression: exp.Select) -> str:
2609        into = expression.args.get("into")
2610        if not self.SUPPORTS_SELECT_INTO and into:
2611            into.pop()
2612
2613        hint = self.sql(expression, "hint")
2614        distinct = self.sql(expression, "distinct")
2615        distinct = f" {distinct}" if distinct else ""
2616        kind = self.sql(expression, "kind")
2617
2618        limit = expression.args.get("limit")
2619        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
2620            top = self.limit_sql(limit, top=True)
2621            limit.pop()
2622        else:
2623            top = ""
2624
2625        expressions = self.expressions(expression)
2626
2627        if kind:
2628            if kind in self.SELECT_KINDS:
2629                kind = f" AS {kind}"
2630            else:
2631                if kind == "STRUCT":
2632                    expressions = self.expressions(
2633                        sqls=[
2634                            self.sql(
2635                                exp.Struct(
2636                                    expressions=[
2637                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2638                                        if isinstance(e, exp.Alias)
2639                                        else e
2640                                        for e in expression.expressions
2641                                    ]
2642                                )
2643                            )
2644                        ]
2645                    )
2646                kind = ""
2647
2648        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
2649        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
2650
2651        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2652        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2653        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2654        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2655        sql = self.query_modifiers(
2656            expression,
2657            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
2658            self.sql(expression, "into", comment=False),
2659            self.sql(expression, "from", comment=False),
2660        )
2661
2662        # If both the CTE and SELECT clauses have comments, generate the latter earlier
2663        if expression.args.get("with"):
2664            sql = self.maybe_comment(sql, expression)
2665            expression.pop_comments()
2666
2667        sql = self.prepend_ctes(expression, sql)
2668
2669        if not self.SUPPORTS_SELECT_INTO and into:
2670            if into.args.get("temporary"):
2671                table_kind = " TEMPORARY"
2672            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2673                table_kind = " UNLOGGED"
2674            else:
2675                table_kind = ""
2676            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2677
2678        return sql
2679
2680    def schema_sql(self, expression: exp.Schema) -> str:
2681        this = self.sql(expression, "this")
2682        sql = self.schema_columns_sql(expression)
2683        return f"{this} {sql}" if this and sql else this or sql
2684
2685    def schema_columns_sql(self, expression: exp.Schema) -> str:
2686        if expression.expressions:
2687            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2688        return ""
2689
2690    def star_sql(self, expression: exp.Star) -> str:
2691        except_ = self.expressions(expression, key="except", flat=True)
2692        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
2693        replace = self.expressions(expression, key="replace", flat=True)
2694        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
2695        rename = self.expressions(expression, key="rename", flat=True)
2696        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
2697        return f"*{except_}{replace}{rename}"
2698
2699    def parameter_sql(self, expression: exp.Parameter) -> str:
2700        this = self.sql(expression, "this")
2701        return f"{self.PARAMETER_TOKEN}{this}"
2702
2703    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2704        this = self.sql(expression, "this")
2705        kind = expression.text("kind")
2706        if kind:
2707            kind = f"{kind}."
2708        return f"@@{kind}{this}"
2709
2710    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2711        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
2712
2713    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2714        alias = self.sql(expression, "alias")
2715        alias = f"{sep}{alias}" if alias else ""
2716        sample = self.sql(expression, "sample")
2717        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
2718            alias = f"{sample}{alias}"
2719
2720            # Set to None so it's not generated again by self.query_modifiers()
2721            expression.set("sample", None)
2722
2723        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2724        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2725        return self.prepend_ctes(expression, sql)
2726
2727    def qualify_sql(self, expression: exp.Qualify) -> str:
2728        this = self.indent(self.sql(expression, "this"))
2729        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2730
2731    def unnest_sql(self, expression: exp.Unnest) -> str:
2732        args = self.expressions(expression, flat=True)
2733
2734        alias = expression.args.get("alias")
2735        offset = expression.args.get("offset")
2736
2737        if self.UNNEST_WITH_ORDINALITY:
2738            if alias and isinstance(offset, exp.Expression):
2739                alias.append("columns", offset)
2740
2741        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2742            columns = alias.columns
2743            alias = self.sql(columns[0]) if columns else ""
2744        else:
2745            alias = self.sql(alias)
2746
2747        alias = f" AS {alias}" if alias else alias
2748        if self.UNNEST_WITH_ORDINALITY:
2749            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2750        else:
2751            if isinstance(offset, exp.Expression):
2752                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2753            elif offset:
2754                suffix = f"{alias} WITH OFFSET"
2755            else:
2756                suffix = alias
2757
2758        return f"UNNEST({args}){suffix}"
2759
2760    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2761        return ""
2762
2763    def where_sql(self, expression: exp.Where) -> str:
2764        this = self.indent(self.sql(expression, "this"))
2765        return f"{self.seg('WHERE')}{self.sep()}{this}"
2766
2767    def window_sql(self, expression: exp.Window) -> str:
2768        this = self.sql(expression, "this")
2769        partition = self.partition_by_sql(expression)
2770        order = expression.args.get("order")
2771        order = self.order_sql(order, flat=True) if order else ""
2772        spec = self.sql(expression, "spec")
2773        alias = self.sql(expression, "alias")
2774        over = self.sql(expression, "over") or "OVER"
2775
2776        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2777
2778        first = expression.args.get("first")
2779        if first is None:
2780            first = ""
2781        else:
2782            first = "FIRST" if first else "LAST"
2783
2784        if not partition and not order and not spec and alias:
2785            return f"{this} {alias}"
2786
2787        args = self.format_args(
2788            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
2789        )
2790        return f"{this} ({args})"
2791
2792    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2793        partition = self.expressions(expression, key="partition_by", flat=True)
2794        return f"PARTITION BY {partition}" if partition else ""
2795
2796    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2797        kind = self.sql(expression, "kind")
2798        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2799        end = (
2800            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2801            or "CURRENT ROW"
2802        )
2803        return f"{kind} BETWEEN {start} AND {end}"
2804
2805    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2806        this = self.sql(expression, "this")
2807        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2808        return f"{this} WITHIN GROUP ({expression_sql})"
2809
2810    def between_sql(self, expression: exp.Between) -> str:
2811        this = self.sql(expression, "this")
2812        low = self.sql(expression, "low")
2813        high = self.sql(expression, "high")
2814        return f"{this} BETWEEN {low} AND {high}"
2815
2816    def bracket_offset_expressions(
2817        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
2818    ) -> t.List[exp.Expression]:
2819        return apply_index_offset(
2820            expression.this,
2821            expression.expressions,
2822            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
2823            dialect=self.dialect,
2824        )
2825
2826    def bracket_sql(self, expression: exp.Bracket) -> str:
2827        expressions = self.bracket_offset_expressions(expression)
2828        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2829        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2830
2831    def all_sql(self, expression: exp.All) -> str:
2832        return f"ALL {self.wrap(expression)}"
2833
2834    def any_sql(self, expression: exp.Any) -> str:
2835        this = self.sql(expression, "this")
2836        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
2837            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2838                this = self.wrap(this)
2839            return f"ANY{this}"
2840        return f"ANY {this}"
2841
2842    def exists_sql(self, expression: exp.Exists) -> str:
2843        return f"EXISTS{self.wrap(expression)}"
2844
2845    def case_sql(self, expression: exp.Case) -> str:
2846        this = self.sql(expression, "this")
2847        statements = [f"CASE {this}" if this else "CASE"]
2848
2849        for e in expression.args["ifs"]:
2850            statements.append(f"WHEN {self.sql(e, 'this')}")
2851            statements.append(f"THEN {self.sql(e, 'true')}")
2852
2853        default = self.sql(expression, "default")
2854
2855        if default:
2856            statements.append(f"ELSE {default}")
2857
2858        statements.append("END")
2859
2860        if self.pretty and self.too_wide(statements):
2861            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2862
2863        return " ".join(statements)
2864
2865    def constraint_sql(self, expression: exp.Constraint) -> str:
2866        this = self.sql(expression, "this")
2867        expressions = self.expressions(expression, flat=True)
2868        return f"CONSTRAINT {this} {expressions}"
2869
2870    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2871        order = expression.args.get("order")
2872        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2873        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2874
2875    def extract_sql(self, expression: exp.Extract) -> str:
2876        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2877        expression_sql = self.sql(expression, "expression")
2878        return f"EXTRACT({this} FROM {expression_sql})"
2879
2880    def trim_sql(self, expression: exp.Trim) -> str:
2881        trim_type = self.sql(expression, "position")
2882
2883        if trim_type == "LEADING":
2884            func_name = "LTRIM"
2885        elif trim_type == "TRAILING":
2886            func_name = "RTRIM"
2887        else:
2888            func_name = "TRIM"
2889
2890        return self.func(func_name, expression.this, expression.expression)
2891
2892    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2893        args = expression.expressions
2894        if isinstance(expression, exp.ConcatWs):
2895            args = args[1:]  # Skip the delimiter
2896
2897        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2898            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
2899
2900        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2901            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2902
2903        return args
2904
2905    def concat_sql(self, expression: exp.Concat) -> str:
2906        expressions = self.convert_concat_args(expression)
2907
2908        # Some dialects don't allow a single-argument CONCAT call
2909        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2910            return self.sql(expressions[0])
2911
2912        return self.func("CONCAT", *expressions)
2913
2914    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2915        return self.func(
2916            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2917        )
2918
2919    def check_sql(self, expression: exp.Check) -> str:
2920        this = self.sql(expression, key="this")
2921        return f"CHECK ({this})"
2922
2923    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2924        expressions = self.expressions(expression, flat=True)
2925        expressions = f" ({expressions})" if expressions else ""
2926        reference = self.sql(expression, "reference")
2927        reference = f" {reference}" if reference else ""
2928        delete = self.sql(expression, "delete")
2929        delete = f" ON DELETE {delete}" if delete else ""
2930        update = self.sql(expression, "update")
2931        update = f" ON UPDATE {update}" if update else ""
2932        options = self.expressions(expression, key="options", flat=True, sep=" ")
2933        options = f" {options}" if options else ""
2934        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
2935
2936    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2937        expressions = self.expressions(expression, flat=True)
2938        options = self.expressions(expression, key="options", flat=True, sep=" ")
2939        options = f" {options}" if options else ""
2940        return f"PRIMARY KEY ({expressions}){options}"
2941
2942    def if_sql(self, expression: exp.If) -> str:
2943        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
2944
2945    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2946        modifier = expression.args.get("modifier")
2947        modifier = f" {modifier}" if modifier else ""
2948        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
2949
2950    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2951        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
2952
2953    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2954        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2955
2956        if expression.args.get("escape"):
2957            path = self.escape_str(path)
2958
2959        if self.QUOTE_JSON_PATH:
2960            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2961
2962        return path
2963
2964    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2965        if isinstance(expression, exp.JSONPathPart):
2966            transform = self.TRANSFORMS.get(expression.__class__)
2967            if not callable(transform):
2968                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2969                return ""
2970
2971            return transform(self, expression)
2972
2973        if isinstance(expression, int):
2974            return str(expression)
2975
2976        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2977            escaped = expression.replace("'", "\\'")
2978            escaped = f"\\'{expression}\\'"
2979        else:
2980            escaped = expression.replace('"', '\\"')
2981            escaped = f'"{escaped}"'
2982
2983        return escaped
2984
2985    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2986        return f"{self.sql(expression, 'this')} FORMAT JSON"
2987
2988    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2989        null_handling = expression.args.get("null_handling")
2990        null_handling = f" {null_handling}" if null_handling else ""
2991
2992        unique_keys = expression.args.get("unique_keys")
2993        if unique_keys is not None:
2994            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2995        else:
2996            unique_keys = ""
2997
2998        return_type = self.sql(expression, "return_type")
2999        return_type = f" RETURNING {return_type}" if return_type else ""
3000        encoding = self.sql(expression, "encoding")
3001        encoding = f" ENCODING {encoding}" if encoding else ""
3002
3003        return self.func(
3004            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
3005            *expression.expressions,
3006            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
3007        )
3008
3009    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
3010        return self.jsonobject_sql(expression)
3011
3012    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
3013        null_handling = expression.args.get("null_handling")
3014        null_handling = f" {null_handling}" if null_handling else ""
3015        return_type = self.sql(expression, "return_type")
3016        return_type = f" RETURNING {return_type}" if return_type else ""
3017        strict = " STRICT" if expression.args.get("strict") else ""
3018        return self.func(
3019            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
3020        )
3021
3022    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
3023        this = self.sql(expression, "this")
3024        order = self.sql(expression, "order")
3025        null_handling = expression.args.get("null_handling")
3026        null_handling = f" {null_handling}" if null_handling else ""
3027        return_type = self.sql(expression, "return_type")
3028        return_type = f" RETURNING {return_type}" if return_type else ""
3029        strict = " STRICT" if expression.args.get("strict") else ""
3030        return self.func(
3031            "JSON_ARRAYAGG",
3032            this,
3033            suffix=f"{order}{null_handling}{return_type}{strict})",
3034        )
3035
3036    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
3037        path = self.sql(expression, "path")
3038        path = f" PATH {path}" if path else ""
3039        nested_schema = self.sql(expression, "nested_schema")
3040
3041        if nested_schema:
3042            return f"NESTED{path} {nested_schema}"
3043
3044        this = self.sql(expression, "this")
3045        kind = self.sql(expression, "kind")
3046        kind = f" {kind}" if kind else ""
3047        return f"{this}{kind}{path}"
3048
3049    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
3050        return self.func("COLUMNS", *expression.expressions)
3051
3052    def jsontable_sql(self, expression: exp.JSONTable) -> str:
3053        this = self.sql(expression, "this")
3054        path = self.sql(expression, "path")
3055        path = f", {path}" if path else ""
3056        error_handling = expression.args.get("error_handling")
3057        error_handling = f" {error_handling}" if error_handling else ""
3058        empty_handling = expression.args.get("empty_handling")
3059        empty_handling = f" {empty_handling}" if empty_handling else ""
3060        schema = self.sql(expression, "schema")
3061        return self.func(
3062            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
3063        )
3064
3065    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
3066        this = self.sql(expression, "this")
3067        kind = self.sql(expression, "kind")
3068        path = self.sql(expression, "path")
3069        path = f" {path}" if path else ""
3070        as_json = " AS JSON" if expression.args.get("as_json") else ""
3071        return f"{this} {kind}{path}{as_json}"
3072
3073    def openjson_sql(self, expression: exp.OpenJSON) -> str:
3074        this = self.sql(expression, "this")
3075        path = self.sql(expression, "path")
3076        path = f", {path}" if path else ""
3077        expressions = self.expressions(expression)
3078        with_ = (
3079            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
3080            if expressions
3081            else ""
3082        )
3083        return f"OPENJSON({this}{path}){with_}"
3084
3085    def in_sql(self, expression: exp.In) -> str:
3086        query = expression.args.get("query")
3087        unnest = expression.args.get("unnest")
3088        field = expression.args.get("field")
3089        is_global = " GLOBAL" if expression.args.get("is_global") else ""
3090
3091        if query:
3092            in_sql = self.sql(query)
3093        elif unnest:
3094            in_sql = self.in_unnest_op(unnest)
3095        elif field:
3096            in_sql = self.sql(field)
3097        else:
3098            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
3099
3100        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
3101
3102    def in_unnest_op(self, unnest: exp.Unnest) -> str:
3103        return f"(SELECT {self.sql(unnest)})"
3104
3105    def interval_sql(self, expression: exp.Interval) -> str:
3106        unit = self.sql(expression, "unit")
3107        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
3108            unit = self.TIME_PART_SINGULARS.get(unit, unit)
3109        unit = f" {unit}" if unit else ""
3110
3111        if self.SINGLE_STRING_INTERVAL:
3112            this = expression.this.name if expression.this else ""
3113            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
3114
3115        this = self.sql(expression, "this")
3116        if this:
3117            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
3118            this = f" {this}" if unwrapped else f" ({this})"
3119
3120        return f"INTERVAL{this}{unit}"
3121
3122    def return_sql(self, expression: exp.Return) -> str:
3123        return f"RETURN {self.sql(expression, 'this')}"
3124
3125    def reference_sql(self, expression: exp.Reference) -> str:
3126        this = self.sql(expression, "this")
3127        expressions = self.expressions(expression, flat=True)
3128        expressions = f"({expressions})" if expressions else ""
3129        options = self.expressions(expression, key="options", flat=True, sep=" ")
3130        options = f" {options}" if options else ""
3131        return f"REFERENCES {this}{expressions}{options}"
3132
3133    def anonymous_sql(self, expression: exp.Anonymous) -> str:
3134        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
3135        parent = expression.parent
3136        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
3137        return self.func(
3138            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
3139        )
3140
3141    def paren_sql(self, expression: exp.Paren) -> str:
3142        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
3143        return f"({sql}{self.seg(')', sep='')}"
3144
3145    def neg_sql(self, expression: exp.Neg) -> str:
3146        # This makes sure we don't convert "- - 5" to "--5", which is a comment
3147        this_sql = self.sql(expression, "this")
3148        sep = " " if this_sql[0] == "-" else ""
3149        return f"-{sep}{this_sql}"
3150
3151    def not_sql(self, expression: exp.Not) -> str:
3152        return f"NOT {self.sql(expression, 'this')}"
3153
3154    def alias_sql(self, expression: exp.Alias) -> str:
3155        alias = self.sql(expression, "alias")
3156        alias = f" AS {alias}" if alias else ""
3157        return f"{self.sql(expression, 'this')}{alias}"
3158
3159    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
3160        alias = expression.args["alias"]
3161
3162        parent = expression.parent
3163        pivot = parent and parent.parent
3164
3165        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
3166            identifier_alias = isinstance(alias, exp.Identifier)
3167            literal_alias = isinstance(alias, exp.Literal)
3168
3169            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3170                alias.replace(exp.Literal.string(alias.output_name))
3171            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3172                alias.replace(exp.to_identifier(alias.output_name))
3173
3174        return self.alias_sql(expression)
3175
3176    def aliases_sql(self, expression: exp.Aliases) -> str:
3177        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
3178
3179    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
3180        this = self.sql(expression, "this")
3181        index = self.sql(expression, "expression")
3182        return f"{this} AT {index}"
3183
3184    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
3185        this = self.sql(expression, "this")
3186        zone = self.sql(expression, "zone")
3187        return f"{this} AT TIME ZONE {zone}"
3188
3189    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
3190        this = self.sql(expression, "this")
3191        zone = self.sql(expression, "zone")
3192        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
3193
3194    def add_sql(self, expression: exp.Add) -> str:
3195        return self.binary(expression, "+")
3196
3197    def and_sql(
3198        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
3199    ) -> str:
3200        return self.connector_sql(expression, "AND", stack)
3201
3202    def or_sql(
3203        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
3204    ) -> str:
3205        return self.connector_sql(expression, "OR", stack)
3206
3207    def xor_sql(
3208        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
3209    ) -> str:
3210        return self.connector_sql(expression, "XOR", stack)
3211
3212    def connector_sql(
3213        self,
3214        expression: exp.Connector,
3215        op: str,
3216        stack: t.Optional[t.List[str | exp.Expression]] = None,
3217    ) -> str:
3218        if stack is not None:
3219            if expression.expressions:
3220                stack.append(self.expressions(expression, sep=f" {op} "))
3221            else:
3222                stack.append(expression.right)
3223                if expression.comments and self.comments:
3224                    for comment in expression.comments:
3225                        if comment:
3226                            op += f" /*{self.pad_comment(comment)}*/"
3227                stack.extend((op, expression.left))
3228            return op
3229
3230        stack = [expression]
3231        sqls: t.List[str] = []
3232        ops = set()
3233
3234        while stack:
3235            node = stack.pop()
3236            if isinstance(node, exp.Connector):
3237                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
3238            else:
3239                sql = self.sql(node)
3240                if sqls and sqls[-1] in ops:
3241                    sqls[-1] += f" {sql}"
3242                else:
3243                    sqls.append(sql)
3244
3245        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
3246        return sep.join(sqls)
3247
3248    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
3249        return self.binary(expression, "&")
3250
3251    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
3252        return self.binary(expression, "<<")
3253
3254    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
3255        return f"~{self.sql(expression, 'this')}"
3256
3257    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
3258        return self.binary(expression, "|")
3259
3260    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
3261        return self.binary(expression, ">>")
3262
3263    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
3264        return self.binary(expression, "^")
3265
3266    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
3267        format_sql = self.sql(expression, "format")
3268        format_sql = f" FORMAT {format_sql}" if format_sql else ""
3269        to_sql = self.sql(expression, "to")
3270        to_sql = f" {to_sql}" if to_sql else ""
3271        action = self.sql(expression, "action")
3272        action = f" {action}" if action else ""
3273        default = self.sql(expression, "default")
3274        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
3275        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
3276
3277    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
3278        zone = self.sql(expression, "this")
3279        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
3280
3281    def collate_sql(self, expression: exp.Collate) -> str:
3282        if self.COLLATE_IS_FUNC:
3283            return self.function_fallback_sql(expression)
3284        return self.binary(expression, "COLLATE")
3285
3286    def command_sql(self, expression: exp.Command) -> str:
3287        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
3288
3289    def comment_sql(self, expression: exp.Comment) -> str:
3290        this = self.sql(expression, "this")
3291        kind = expression.args["kind"]
3292        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
3293        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
3294        expression_sql = self.sql(expression, "expression")
3295        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
3296
3297    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
3298        this = self.sql(expression, "this")
3299        delete = " DELETE" if expression.args.get("delete") else ""
3300        recompress = self.sql(expression, "recompress")
3301        recompress = f" RECOMPRESS {recompress}" if recompress else ""
3302        to_disk = self.sql(expression, "to_disk")
3303        to_disk = f" TO DISK {to_disk}" if to_disk else ""
3304        to_volume = self.sql(expression, "to_volume")
3305        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
3306        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
3307
3308    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
3309        where = self.sql(expression, "where")
3310        group = self.sql(expression, "group")
3311        aggregates = self.expressions(expression, key="aggregates")
3312        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
3313
3314        if not (where or group or aggregates) and len(expression.expressions) == 1:
3315            return f"TTL {self.expressions(expression, flat=True)}"
3316
3317        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
3318
3319    def transaction_sql(self, expression: exp.Transaction) -> str:
3320        return "BEGIN"
3321
3322    def commit_sql(self, expression: exp.Commit) -> str:
3323        chain = expression.args.get("chain")
3324        if chain is not None:
3325            chain = " AND CHAIN" if chain else " AND NO CHAIN"
3326
3327        return f"COMMIT{chain or ''}"
3328
3329    def rollback_sql(self, expression: exp.Rollback) -> str:
3330        savepoint = expression.args.get("savepoint")
3331        savepoint = f" TO {savepoint}" if savepoint else ""
3332        return f"ROLLBACK{savepoint}"
3333
3334    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
3335        this = self.sql(expression, "this")
3336
3337        dtype = self.sql(expression, "dtype")
3338        if dtype:
3339            collate = self.sql(expression, "collate")
3340            collate = f" COLLATE {collate}" if collate else ""
3341            using = self.sql(expression, "using")
3342            using = f" USING {using}" if using else ""
3343            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
3344
3345        default = self.sql(expression, "default")
3346        if default:
3347            return f"ALTER COLUMN {this} SET DEFAULT {default}"
3348
3349        comment = self.sql(expression, "comment")
3350        if comment:
3351            return f"ALTER COLUMN {this} COMMENT {comment}"
3352
3353        visible = expression.args.get("visible")
3354        if visible:
3355            return f"ALTER COLUMN {this} SET {visible}"
3356
3357        allow_null = expression.args.get("allow_null")
3358        drop = expression.args.get("drop")
3359
3360        if not drop and not allow_null:
3361            self.unsupported("Unsupported ALTER COLUMN syntax")
3362
3363        if allow_null is not None:
3364            keyword = "DROP" if drop else "SET"
3365            return f"ALTER COLUMN {this} {keyword} NOT NULL"
3366
3367        return f"ALTER COLUMN {this} DROP DEFAULT"
3368
3369    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3370        this = self.sql(expression, "this")
3371
3372        visible = expression.args.get("visible")
3373        visible_sql = "VISIBLE" if visible else "INVISIBLE"
3374
3375        return f"ALTER INDEX {this} {visible_sql}"
3376
3377    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
3378        this = self.sql(expression, "this")
3379        if not isinstance(expression.this, exp.Var):
3380            this = f"KEY DISTKEY {this}"
3381        return f"ALTER DISTSTYLE {this}"
3382
3383    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
3384        compound = " COMPOUND" if expression.args.get("compound") else ""
3385        this = self.sql(expression, "this")
3386        expressions = self.expressions(expression, flat=True)
3387        expressions = f"({expressions})" if expressions else ""
3388        return f"ALTER{compound} SORTKEY {this or expressions}"
3389
3390    def alterrename_sql(self, expression: exp.AlterRename) -> str:
3391        if not self.RENAME_TABLE_WITH_DB:
3392            # Remove db from tables
3393            expression = expression.transform(
3394                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3395            ).assert_is(exp.AlterRename)
3396        this = self.sql(expression, "this")
3397        return f"RENAME TO {this}"
3398
3399    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3400        exists = " IF EXISTS" if expression.args.get("exists") else ""
3401        old_column = self.sql(expression, "this")
3402        new_column = self.sql(expression, "to")
3403        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
3404
3405    def alterset_sql(self, expression: exp.AlterSet) -> str:
3406        exprs = self.expressions(expression, flat=True)
3407        return f"SET {exprs}"
3408
3409    def alter_sql(self, expression: exp.Alter) -> str:
3410        actions = expression.args["actions"]
3411
3412        if isinstance(actions[0], exp.ColumnDef):
3413            actions = self.add_column_sql(expression)
3414        elif isinstance(actions[0], exp.Schema):
3415            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
3416        elif isinstance(actions[0], exp.Delete):
3417            actions = self.expressions(expression, key="actions", flat=True)
3418        elif isinstance(actions[0], exp.Query):
3419            actions = "AS " + self.expressions(expression, key="actions")
3420        else:
3421            actions = self.expressions(expression, key="actions", flat=True)
3422
3423        exists = " IF EXISTS" if expression.args.get("exists") else ""
3424        on_cluster = self.sql(expression, "cluster")
3425        on_cluster = f" {on_cluster}" if on_cluster else ""
3426        only = " ONLY" if expression.args.get("only") else ""
3427        options = self.expressions(expression, key="options")
3428        options = f", {options}" if options else ""
3429        kind = self.sql(expression, "kind")
3430        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3431
3432        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
3433
3434    def add_column_sql(self, expression: exp.Alter) -> str:
3435        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
3436            return self.expressions(
3437                expression,
3438                key="actions",
3439                prefix="ADD COLUMN ",
3440                skip_first=True,
3441            )
3442        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
3443
3444    def droppartition_sql(self, expression: exp.DropPartition) -> str:
3445        expressions = self.expressions(expression)
3446        exists = " IF EXISTS " if expression.args.get("exists") else " "
3447        return f"DROP{exists}{expressions}"
3448
3449    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
3450        return f"ADD {self.expressions(expression)}"
3451
3452    def distinct_sql(self, expression: exp.Distinct) -> str:
3453        this = self.expressions(expression, flat=True)
3454
3455        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
3456            case = exp.case()
3457            for arg in expression.expressions:
3458                case = case.when(arg.is_(exp.null()), exp.null())
3459            this = self.sql(case.else_(f"({this})"))
3460
3461        this = f" {this}" if this else ""
3462
3463        on = self.sql(expression, "on")
3464        on = f" ON {on}" if on else ""
3465        return f"DISTINCT{this}{on}"
3466
3467    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
3468        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
3469
3470    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
3471        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
3472
3473    def havingmax_sql(self, expression: exp.HavingMax) -> str:
3474        this_sql = self.sql(expression, "this")
3475        expression_sql = self.sql(expression, "expression")
3476        kind = "MAX" if expression.args.get("max") else "MIN"
3477        return f"{this_sql} HAVING {kind} {expression_sql}"
3478
3479    def intdiv_sql(self, expression: exp.IntDiv) -> str:
3480        return self.sql(
3481            exp.Cast(
3482                this=exp.Div(this=expression.this, expression=expression.expression),
3483                to=exp.DataType(this=exp.DataType.Type.INT),
3484            )
3485        )
3486
3487    def dpipe_sql(self, expression: exp.DPipe) -> str:
3488        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
3489            return self.func(
3490                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
3491            )
3492        return self.binary(expression, "||")
3493
3494    def div_sql(self, expression: exp.Div) -> str:
3495        l, r = expression.left, expression.right
3496
3497        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
3498            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
3499
3500        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
3501            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
3502                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
3503
3504        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
3505            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
3506                return self.sql(
3507                    exp.cast(
3508                        l / r,
3509                        to=exp.DataType.Type.BIGINT,
3510                    )
3511                )
3512
3513        return self.binary(expression, "/")
3514
3515    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
3516        n = exp._wrap(expression.this, exp.Binary)
3517        d = exp._wrap(expression.expression, exp.Binary)
3518        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
3519
3520    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3521        return self.binary(expression, "OVERLAPS")
3522
3523    def distance_sql(self, expression: exp.Distance) -> str:
3524        return self.binary(expression, "<->")
3525
3526    def dot_sql(self, expression: exp.Dot) -> str:
3527        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
3528
3529    def eq_sql(self, expression: exp.EQ) -> str:
3530        return self.binary(expression, "=")
3531
3532    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3533        return self.binary(expression, ":=")
3534
3535    def escape_sql(self, expression: exp.Escape) -> str:
3536        return self.binary(expression, "ESCAPE")
3537
3538    def glob_sql(self, expression: exp.Glob) -> str:
3539        return self.binary(expression, "GLOB")
3540
3541    def gt_sql(self, expression: exp.GT) -> str:
3542        return self.binary(expression, ">")
3543
3544    def gte_sql(self, expression: exp.GTE) -> str:
3545        return self.binary(expression, ">=")
3546
3547    def ilike_sql(self, expression: exp.ILike) -> str:
3548        return self.binary(expression, "ILIKE")
3549
3550    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
3551        return self.binary(expression, "ILIKE ANY")
3552
3553    def is_sql(self, expression: exp.Is) -> str:
3554        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3555            return self.sql(
3556                expression.this if expression.expression.this else exp.not_(expression.this)
3557            )
3558        return self.binary(expression, "IS")
3559
3560    def like_sql(self, expression: exp.Like) -> str:
3561        return self.binary(expression, "LIKE")
3562
3563    def likeany_sql(self, expression: exp.LikeAny) -> str:
3564        return self.binary(expression, "LIKE ANY")
3565
3566    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3567        return self.binary(expression, "SIMILAR TO")
3568
3569    def lt_sql(self, expression: exp.LT) -> str:
3570        return self.binary(expression, "<")
3571
3572    def lte_sql(self, expression: exp.LTE) -> str:
3573        return self.binary(expression, "<=")
3574
3575    def mod_sql(self, expression: exp.Mod) -> str:
3576        return self.binary(expression, "%")
3577
3578    def mul_sql(self, expression: exp.Mul) -> str:
3579        return self.binary(expression, "*")
3580
3581    def neq_sql(self, expression: exp.NEQ) -> str:
3582        return self.binary(expression, "<>")
3583
3584    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3585        return self.binary(expression, "IS NOT DISTINCT FROM")
3586
3587    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3588        return self.binary(expression, "IS DISTINCT FROM")
3589
3590    def slice_sql(self, expression: exp.Slice) -> str:
3591        return self.binary(expression, ":")
3592
3593    def sub_sql(self, expression: exp.Sub) -> str:
3594        return self.binary(expression, "-")
3595
3596    def trycast_sql(self, expression: exp.TryCast) -> str:
3597        return self.cast_sql(expression, safe_prefix="TRY_")
3598
3599    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
3600        return self.cast_sql(expression)
3601
3602    def try_sql(self, expression: exp.Try) -> str:
3603        if not self.TRY_SUPPORTED:
3604            self.unsupported("Unsupported TRY function")
3605            return self.sql(expression, "this")
3606
3607        return self.func("TRY", expression.this)
3608
3609    def log_sql(self, expression: exp.Log) -> str:
3610        this = expression.this
3611        expr = expression.expression
3612
3613        if self.dialect.LOG_BASE_FIRST is False:
3614            this, expr = expr, this
3615        elif self.dialect.LOG_BASE_FIRST is None and expr:
3616            if this.name in ("2", "10"):
3617                return self.func(f"LOG{this.name}", expr)
3618
3619            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
3620
3621        return self.func("LOG", this, expr)
3622
3623    def use_sql(self, expression: exp.Use) -> str:
3624        kind = self.sql(expression, "kind")
3625        kind = f" {kind}" if kind else ""
3626        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
3627        this = f" {this}" if this else ""
3628        return f"USE{kind}{this}"
3629
3630    def binary(self, expression: exp.Binary, op: str) -> str:
3631        sqls: t.List[str] = []
3632        stack: t.List[t.Union[str, exp.Expression]] = [expression]
3633        binary_type = type(expression)
3634
3635        while stack:
3636            node = stack.pop()
3637
3638            if type(node) is binary_type:
3639                op_func = node.args.get("operator")
3640                if op_func:
3641                    op = f"OPERATOR({self.sql(op_func)})"
3642
3643                stack.append(node.right)
3644                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
3645                stack.append(node.left)
3646            else:
3647                sqls.append(self.sql(node))
3648
3649        return "".join(sqls)
3650
3651    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
3652        to_clause = self.sql(expression, "to")
3653        if to_clause:
3654            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
3655
3656        return self.function_fallback_sql(expression)
3657
3658    def function_fallback_sql(self, expression: exp.Func) -> str:
3659        args = []
3660
3661        for key in expression.arg_types:
3662            arg_value = expression.args.get(key)
3663
3664            if isinstance(arg_value, list):
3665                for value in arg_value:
3666                    args.append(value)
3667            elif arg_value is not None:
3668                args.append(arg_value)
3669
3670        if self.dialect.PRESERVE_ORIGINAL_NAMES:
3671            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3672        else:
3673            name = expression.sql_name()
3674
3675        return self.func(name, *args)
3676
3677    def func(
3678        self,
3679        name: str,
3680        *args: t.Optional[exp.Expression | str],
3681        prefix: str = "(",
3682        suffix: str = ")",
3683        normalize: bool = True,
3684    ) -> str:
3685        name = self.normalize_func(name) if normalize else name
3686        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
3687
3688    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
3689        arg_sqls = tuple(
3690            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
3691        )
3692        if self.pretty and self.too_wide(arg_sqls):
3693            return self.indent(
3694                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
3695            )
3696        return sep.join(arg_sqls)
3697
3698    def too_wide(self, args: t.Iterable) -> bool:
3699        return sum(len(arg) for arg in args) > self.max_text_width
3700
3701    def format_time(
3702        self,
3703        expression: exp.Expression,
3704        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
3705        inverse_time_trie: t.Optional[t.Dict] = None,
3706    ) -> t.Optional[str]:
3707        return format_time(
3708            self.sql(expression, "format"),
3709            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
3710            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
3711        )
3712
3713    def expressions(
3714        self,
3715        expression: t.Optional[exp.Expression] = None,
3716        key: t.Optional[str] = None,
3717        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3718        flat: bool = False,
3719        indent: bool = True,
3720        skip_first: bool = False,
3721        skip_last: bool = False,
3722        sep: str = ", ",
3723        prefix: str = "",
3724        dynamic: bool = False,
3725        new_line: bool = False,
3726    ) -> str:
3727        expressions = expression.args.get(key or "expressions") if expression else sqls
3728
3729        if not expressions:
3730            return ""
3731
3732        if flat:
3733            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3734
3735        num_sqls = len(expressions)
3736        result_sqls = []
3737
3738        for i, e in enumerate(expressions):
3739            sql = self.sql(e, comment=False)
3740            if not sql:
3741                continue
3742
3743            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3744
3745            if self.pretty:
3746                if self.leading_comma:
3747                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
3748                else:
3749                    result_sqls.append(
3750                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
3751                    )
3752            else:
3753                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3754
3755        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
3756            if new_line:
3757                result_sqls.insert(0, "")
3758                result_sqls.append("")
3759            result_sql = "\n".join(s.rstrip() for s in result_sqls)
3760        else:
3761            result_sql = "".join(result_sqls)
3762
3763        return (
3764            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
3765            if indent
3766            else result_sql
3767        )
3768
3769    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3770        flat = flat or isinstance(expression.parent, exp.Properties)
3771        expressions_sql = self.expressions(expression, flat=flat)
3772        if flat:
3773            return f"{op} {expressions_sql}"
3774        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3775
3776    def naked_property(self, expression: exp.Property) -> str:
3777        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3778        if not property_name:
3779            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3780        return f"{property_name} {self.sql(expression, 'this')}"
3781
3782    def tag_sql(self, expression: exp.Tag) -> str:
3783        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3784
3785    def token_sql(self, token_type: TokenType) -> str:
3786        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3787
3788    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3789        this = self.sql(expression, "this")
3790        expressions = self.no_identify(self.expressions, expression)
3791        expressions = (
3792            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3793        )
3794        return f"{this}{expressions}" if expressions.strip() != "" else this
3795
3796    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3797        this = self.sql(expression, "this")
3798        expressions = self.expressions(expression, flat=True)
3799        return f"{this}({expressions})"
3800
3801    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3802        return self.binary(expression, "=>")
3803
3804    def when_sql(self, expression: exp.When) -> str:
3805        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3806        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3807        condition = self.sql(expression, "condition")
3808        condition = f" AND {condition}" if condition else ""
3809
3810        then_expression = expression.args.get("then")
3811        if isinstance(then_expression, exp.Insert):
3812            this = self.sql(then_expression, "this")
3813            this = f"INSERT {this}" if this else "INSERT"
3814            then = self.sql(then_expression, "expression")
3815            then = f"{this} VALUES {then}" if then else this
3816        elif isinstance(then_expression, exp.Update):
3817            if isinstance(then_expression.args.get("expressions"), exp.Star):
3818                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3819            else:
3820                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3821        else:
3822            then = self.sql(then_expression)
3823        return f"WHEN {matched}{source}{condition} THEN {then}"
3824
3825    def whens_sql(self, expression: exp.Whens) -> str:
3826        return self.expressions(expression, sep=" ", indent=False)
3827
3828    def merge_sql(self, expression: exp.Merge) -> str:
3829        table = expression.this
3830        table_alias = ""
3831
3832        hints = table.args.get("hints")
3833        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3834            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3835            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3836
3837        this = self.sql(table)
3838        using = f"USING {self.sql(expression, 'using')}"
3839        on = f"ON {self.sql(expression, 'on')}"
3840        whens = self.sql(expression, "whens")
3841
3842        returning = self.sql(expression, "returning")
3843        if returning:
3844            whens = f"{whens}{returning}"
3845
3846        sep = self.sep()
3847
3848        return self.prepend_ctes(
3849            expression,
3850            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
3851        )
3852
3853    @unsupported_args("format")
3854    def tochar_sql(self, expression: exp.ToChar) -> str:
3855        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
3856
3857    def tonumber_sql(self, expression: exp.ToNumber) -> str:
3858        if not self.SUPPORTS_TO_NUMBER:
3859            self.unsupported("Unsupported TO_NUMBER function")
3860            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
3861
3862        fmt = expression.args.get("format")
3863        if not fmt:
3864            self.unsupported("Conversion format is required for TO_NUMBER")
3865            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
3866
3867        return self.func("TO_NUMBER", expression.this, fmt)
3868
3869    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3870        this = self.sql(expression, "this")
3871        kind = self.sql(expression, "kind")
3872        settings_sql = self.expressions(expression, key="settings", sep=" ")
3873        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3874        return f"{this}({kind}{args})"
3875
3876    def dictrange_sql(self, expression: exp.DictRange) -> str:
3877        this = self.sql(expression, "this")
3878        max = self.sql(expression, "max")
3879        min = self.sql(expression, "min")
3880        return f"{this}(MIN {min} MAX {max})"
3881
3882    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3883        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
3884
3885    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
3886        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
3887
3888    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
3889    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
3890        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
3891
3892    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
3893    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
3894        expressions = self.expressions(expression, flat=True)
3895        expressions = f" {self.wrap(expressions)}" if expressions else ""
3896        buckets = self.sql(expression, "buckets")
3897        kind = self.sql(expression, "kind")
3898        buckets = f" BUCKETS {buckets}" if buckets else ""
3899        order = self.sql(expression, "order")
3900        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
3901
3902    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3903        return ""
3904
3905    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3906        expressions = self.expressions(expression, key="expressions", flat=True)
3907        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3908        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3909        buckets = self.sql(expression, "buckets")
3910        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3911
3912    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3913        this = self.sql(expression, "this")
3914        having = self.sql(expression, "having")
3915
3916        if having:
3917            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3918
3919        return self.func("ANY_VALUE", this)
3920
3921    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3922        transform = self.func("TRANSFORM", *expression.expressions)
3923        row_format_before = self.sql(expression, "row_format_before")
3924        row_format_before = f" {row_format_before}" if row_format_before else ""
3925        record_writer = self.sql(expression, "record_writer")
3926        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3927        using = f" USING {self.sql(expression, 'command_script')}"
3928        schema = self.sql(expression, "schema")
3929        schema = f" AS {schema}" if schema else ""
3930        row_format_after = self.sql(expression, "row_format_after")
3931        row_format_after = f" {row_format_after}" if row_format_after else ""
3932        record_reader = self.sql(expression, "record_reader")
3933        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3934        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3935
3936    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3937        key_block_size = self.sql(expression, "key_block_size")
3938        if key_block_size:
3939            return f"KEY_BLOCK_SIZE = {key_block_size}"
3940
3941        using = self.sql(expression, "using")
3942        if using:
3943            return f"USING {using}"
3944
3945        parser = self.sql(expression, "parser")
3946        if parser:
3947            return f"WITH PARSER {parser}"
3948
3949        comment = self.sql(expression, "comment")
3950        if comment:
3951            return f"COMMENT {comment}"
3952
3953        visible = expression.args.get("visible")
3954        if visible is not None:
3955            return "VISIBLE" if visible else "INVISIBLE"
3956
3957        engine_attr = self.sql(expression, "engine_attr")
3958        if engine_attr:
3959            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3960
3961        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3962        if secondary_engine_attr:
3963            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3964
3965        self.unsupported("Unsupported index constraint option.")
3966        return ""
3967
3968    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3969        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3970        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
3971
3972    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3973        kind = self.sql(expression, "kind")
3974        kind = f"{kind} INDEX" if kind else "INDEX"
3975        this = self.sql(expression, "this")
3976        this = f" {this}" if this else ""
3977        index_type = self.sql(expression, "index_type")
3978        index_type = f" USING {index_type}" if index_type else ""
3979        expressions = self.expressions(expression, flat=True)
3980        expressions = f" ({expressions})" if expressions else ""
3981        options = self.expressions(expression, key="options", sep=" ")
3982        options = f" {options}" if options else ""
3983        return f"{kind}{this}{index_type}{expressions}{options}"
3984
3985    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3986        if self.NVL2_SUPPORTED:
3987            return self.function_fallback_sql(expression)
3988
3989        case = exp.Case().when(
3990            expression.this.is_(exp.null()).not_(copy=False),
3991            expression.args["true"],
3992            copy=False,
3993        )
3994        else_cond = expression.args.get("false")
3995        if else_cond:
3996            case.else_(else_cond, copy=False)
3997
3998        return self.sql(case)
3999
4000    def comprehension_sql(self, expression: exp.Comprehension) -> str:
4001        this = self.sql(expression, "this")
4002        expr = self.sql(expression, "expression")
4003        iterator = self.sql(expression, "iterator")
4004        condition = self.sql(expression, "condition")
4005        condition = f" IF {condition}" if condition else ""
4006        return f"{this} FOR {expr} IN {iterator}{condition}"
4007
4008    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
4009        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
4010
4011    def opclass_sql(self, expression: exp.Opclass) -> str:
4012        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
4013
4014    def predict_sql(self, expression: exp.Predict) -> str:
4015        model = self.sql(expression, "this")
4016        model = f"MODEL {model}"
4017        table = self.sql(expression, "expression")
4018        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4019        parameters = self.sql(expression, "params_struct")
4020        return self.func("PREDICT", model, table, parameters or None)
4021
4022    def forin_sql(self, expression: exp.ForIn) -> str:
4023        this = self.sql(expression, "this")
4024        expression_sql = self.sql(expression, "expression")
4025        return f"FOR {this} DO {expression_sql}"
4026
4027    def refresh_sql(self, expression: exp.Refresh) -> str:
4028        this = self.sql(expression, "this")
4029        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
4030        return f"REFRESH {table}{this}"
4031
4032    def toarray_sql(self, expression: exp.ToArray) -> str:
4033        arg = expression.this
4034        if not arg.type:
4035            from sqlglot.optimizer.annotate_types import annotate_types
4036
4037            arg = annotate_types(arg, dialect=self.dialect)
4038
4039        if arg.is_type(exp.DataType.Type.ARRAY):
4040            return self.sql(arg)
4041
4042        cond_for_null = arg.is_(exp.null())
4043        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
4044
4045    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
4046        this = expression.this
4047        time_format = self.format_time(expression)
4048
4049        if time_format:
4050            return self.sql(
4051                exp.cast(
4052                    exp.StrToTime(this=this, format=expression.args["format"]),
4053                    exp.DataType.Type.TIME,
4054                )
4055            )
4056
4057        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
4058            return self.sql(this)
4059
4060        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
4061
4062    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
4063        this = expression.this
4064        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
4065            return self.sql(this)
4066
4067        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
4068
4069    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
4070        this = expression.this
4071        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
4072            return self.sql(this)
4073
4074        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
4075
4076    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
4077        this = expression.this
4078        time_format = self.format_time(expression)
4079
4080        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
4081            return self.sql(
4082                exp.cast(
4083                    exp.StrToTime(this=this, format=expression.args["format"]),
4084                    exp.DataType.Type.DATE,
4085                )
4086            )
4087
4088        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
4089            return self.sql(this)
4090
4091        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
4092
4093    def unixdate_sql(self, expression: exp.UnixDate) -> str:
4094        return self.sql(
4095            exp.func(
4096                "DATEDIFF",
4097                expression.this,
4098                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
4099                "day",
4100            )
4101        )
4102
4103    def lastday_sql(self, expression: exp.LastDay) -> str:
4104        if self.LAST_DAY_SUPPORTS_DATE_PART:
4105            return self.function_fallback_sql(expression)
4106
4107        unit = expression.text("unit")
4108        if unit and unit != "MONTH":
4109            self.unsupported("Date parts are not supported in LAST_DAY.")
4110
4111        return self.func("LAST_DAY", expression.this)
4112
4113    def dateadd_sql(self, expression: exp.DateAdd) -> str:
4114        from sqlglot.dialects.dialect import unit_to_str
4115
4116        return self.func(
4117            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
4118        )
4119
4120    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
4121        if self.CAN_IMPLEMENT_ARRAY_ANY:
4122            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
4123            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
4124            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
4125            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
4126
4127        from sqlglot.dialects import Dialect
4128
4129        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
4130        if self.dialect.__class__ != Dialect:
4131            self.unsupported("ARRAY_ANY is unsupported")
4132
4133        return self.function_fallback_sql(expression)
4134
4135    def struct_sql(self, expression: exp.Struct) -> str:
4136        expression.set(
4137            "expressions",
4138            [
4139                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
4140                if isinstance(e, exp.PropertyEQ)
4141                else e
4142                for e in expression.expressions
4143            ],
4144        )
4145
4146        return self.function_fallback_sql(expression)
4147
4148    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
4149        low = self.sql(expression, "this")
4150        high = self.sql(expression, "expression")
4151
4152        return f"{low} TO {high}"
4153
4154    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
4155        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
4156        tables = f" {self.expressions(expression)}"
4157
4158        exists = " IF EXISTS" if expression.args.get("exists") else ""
4159
4160        on_cluster = self.sql(expression, "cluster")
4161        on_cluster = f" {on_cluster}" if on_cluster else ""
4162
4163        identity = self.sql(expression, "identity")
4164        identity = f" {identity} IDENTITY" if identity else ""
4165
4166        option = self.sql(expression, "option")
4167        option = f" {option}" if option else ""
4168
4169        partition = self.sql(expression, "partition")
4170        partition = f" {partition}" if partition else ""
4171
4172        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
4173
4174    # This transpiles T-SQL's CONVERT function
4175    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
4176    def convert_sql(self, expression: exp.Convert) -> str:
4177        to = expression.this
4178        value = expression.expression
4179        style = expression.args.get("style")
4180        safe = expression.args.get("safe")
4181        strict = expression.args.get("strict")
4182
4183        if not to or not value:
4184            return ""
4185
4186        # Retrieve length of datatype and override to default if not specified
4187        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4188            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
4189
4190        transformed: t.Optional[exp.Expression] = None
4191        cast = exp.Cast if strict else exp.TryCast
4192
4193        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
4194        if isinstance(style, exp.Literal) and style.is_int:
4195            from sqlglot.dialects.tsql import TSQL
4196
4197            style_value = style.name
4198            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
4199            if not converted_style:
4200                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
4201
4202            fmt = exp.Literal.string(converted_style)
4203
4204            if to.this == exp.DataType.Type.DATE:
4205                transformed = exp.StrToDate(this=value, format=fmt)
4206            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
4207                transformed = exp.StrToTime(this=value, format=fmt)
4208            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4209                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
4210            elif to.this == exp.DataType.Type.TEXT:
4211                transformed = exp.TimeToStr(this=value, format=fmt)
4212
4213        if not transformed:
4214            transformed = cast(this=value, to=to, safe=safe)
4215
4216        return self.sql(transformed)
4217
4218    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
4219        this = expression.this
4220        if isinstance(this, exp.JSONPathWildcard):
4221            this = self.json_path_part(this)
4222            return f".{this}" if this else ""
4223
4224        if exp.SAFE_IDENTIFIER_RE.match(this):
4225            return f".{this}"
4226
4227        this = self.json_path_part(this)
4228        return (
4229            f"[{this}]"
4230            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
4231            else f".{this}"
4232        )
4233
4234    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
4235        this = self.json_path_part(expression.this)
4236        return f"[{this}]" if this else ""
4237
4238    def _simplify_unless_literal(self, expression: E) -> E:
4239        if not isinstance(expression, exp.Literal):
4240            from sqlglot.optimizer.simplify import simplify
4241
4242            expression = simplify(expression, dialect=self.dialect)
4243
4244        return expression
4245
4246    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
4247        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
4248            # The first modifier here will be the one closest to the AggFunc's arg
4249            mods = sorted(
4250                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
4251                key=lambda x: 0
4252                if isinstance(x, exp.HavingMax)
4253                else (1 if isinstance(x, exp.Order) else 2),
4254            )
4255
4256            if mods:
4257                mod = mods[0]
4258                this = expression.__class__(this=mod.this.copy())
4259                this.meta["inline"] = True
4260                mod.this.replace(this)
4261                return self.sql(expression.this)
4262
4263            agg_func = expression.find(exp.AggFunc)
4264
4265            if agg_func:
4266                return self.sql(agg_func)[:-1] + f" {text})"
4267
4268        return f"{self.sql(expression, 'this')} {text}"
4269
4270    def _replace_line_breaks(self, string: str) -> str:
4271        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
4272        if self.pretty:
4273            return string.replace("\n", self.SENTINEL_LINE_BREAK)
4274        return string
4275
4276    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
4277        option = self.sql(expression, "this")
4278
4279        if expression.expressions:
4280            upper = option.upper()
4281
4282            # Snowflake FILE_FORMAT options are separated by whitespace
4283            sep = " " if upper == "FILE_FORMAT" else ", "
4284
4285            # Databricks copy/format options do not set their list of values with EQ
4286            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
4287            values = self.expressions(expression, flat=True, sep=sep)
4288            return f"{option}{op}({values})"
4289
4290        value = self.sql(expression, "expression")
4291
4292        if not value:
4293            return option
4294
4295        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
4296
4297        return f"{option}{op}{value}"
4298
4299    def credentials_sql(self, expression: exp.Credentials) -> str:
4300        cred_expr = expression.args.get("credentials")
4301        if isinstance(cred_expr, exp.Literal):
4302            # Redshift case: CREDENTIALS <string>
4303            credentials = self.sql(expression, "credentials")
4304            credentials = f"CREDENTIALS {credentials}" if credentials else ""
4305        else:
4306            # Snowflake case: CREDENTIALS = (...)
4307            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
4308            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
4309
4310        storage = self.sql(expression, "storage")
4311        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
4312
4313        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
4314        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
4315
4316        iam_role = self.sql(expression, "iam_role")
4317        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
4318
4319        region = self.sql(expression, "region")
4320        region = f" REGION {region}" if region else ""
4321
4322        return f"{credentials}{storage}{encryption}{iam_role}{region}"
4323
4324    def copy_sql(self, expression: exp.Copy) -> str:
4325        this = self.sql(expression, "this")
4326        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
4327
4328        credentials = self.sql(expression, "credentials")
4329        credentials = self.seg(credentials) if credentials else ""
4330        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
4331        files = self.expressions(expression, key="files", flat=True)
4332
4333        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
4334        params = self.expressions(
4335            expression,
4336            key="params",
4337            sep=sep,
4338            new_line=True,
4339            skip_last=True,
4340            skip_first=True,
4341            indent=self.COPY_PARAMS_ARE_WRAPPED,
4342        )
4343
4344        if params:
4345            if self.COPY_PARAMS_ARE_WRAPPED:
4346                params = f" WITH ({params})"
4347            elif not self.pretty:
4348                params = f" {params}"
4349
4350        return f"COPY{this}{kind} {files}{credentials}{params}"
4351
4352    def semicolon_sql(self, expression: exp.Semicolon) -> str:
4353        return ""
4354
4355    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
4356        on_sql = "ON" if expression.args.get("on") else "OFF"
4357        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
4358        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
4359        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
4360        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
4361
4362        if filter_col or retention_period:
4363            on_sql = self.func("ON", filter_col, retention_period)
4364
4365        return f"DATA_DELETION={on_sql}"
4366
4367    def maskingpolicycolumnconstraint_sql(
4368        self, expression: exp.MaskingPolicyColumnConstraint
4369    ) -> str:
4370        this = self.sql(expression, "this")
4371        expressions = self.expressions(expression, flat=True)
4372        expressions = f" USING ({expressions})" if expressions else ""
4373        return f"MASKING POLICY {this}{expressions}"
4374
4375    def gapfill_sql(self, expression: exp.GapFill) -> str:
4376        this = self.sql(expression, "this")
4377        this = f"TABLE {this}"
4378        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
4379
4380    def scope_resolution(self, rhs: str, scope_name: str) -> str:
4381        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
4382
4383    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
4384        this = self.sql(expression, "this")
4385        expr = expression.expression
4386
4387        if isinstance(expr, exp.Func):
4388            # T-SQL's CLR functions are case sensitive
4389            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
4390        else:
4391            expr = self.sql(expression, "expression")
4392
4393        return self.scope_resolution(expr, this)
4394
4395    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
4396        if self.PARSE_JSON_NAME is None:
4397            return self.sql(expression.this)
4398
4399        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
4400
4401    def rand_sql(self, expression: exp.Rand) -> str:
4402        lower = self.sql(expression, "lower")
4403        upper = self.sql(expression, "upper")
4404
4405        if lower and upper:
4406            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
4407        return self.func("RAND", expression.this)
4408
4409    def changes_sql(self, expression: exp.Changes) -> str:
4410        information = self.sql(expression, "information")
4411        information = f"INFORMATION => {information}"
4412        at_before = self.sql(expression, "at_before")
4413        at_before = f"{self.seg('')}{at_before}" if at_before else ""
4414        end = self.sql(expression, "end")
4415        end = f"{self.seg('')}{end}" if end else ""
4416
4417        return f"CHANGES ({information}){at_before}{end}"
4418
4419    def pad_sql(self, expression: exp.Pad) -> str:
4420        prefix = "L" if expression.args.get("is_left") else "R"
4421
4422        fill_pattern = self.sql(expression, "fill_pattern") or None
4423        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
4424            fill_pattern = "' '"
4425
4426        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
4427
4428    def summarize_sql(self, expression: exp.Summarize) -> str:
4429        table = " TABLE" if expression.args.get("table") else ""
4430        return f"SUMMARIZE{table} {self.sql(expression.this)}"
4431
4432    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
4433        generate_series = exp.GenerateSeries(**expression.args)
4434
4435        parent = expression.parent
4436        if isinstance(parent, (exp.Alias, exp.TableAlias)):
4437            parent = parent.parent
4438
4439        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
4440            return self.sql(exp.Unnest(expressions=[generate_series]))
4441
4442        if isinstance(parent, exp.Select):
4443            self.unsupported("GenerateSeries projection unnesting is not supported.")
4444
4445        return self.sql(generate_series)
4446
4447    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
4448        exprs = expression.expressions
4449        if not self.ARRAY_CONCAT_IS_VAR_LEN:
4450            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
4451        else:
4452            rhs = self.expressions(expression)
4453
4454        return self.func(name, expression.this, rhs or None)
4455
4456    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
4457        if self.SUPPORTS_CONVERT_TIMEZONE:
4458            return self.function_fallback_sql(expression)
4459
4460        source_tz = expression.args.get("source_tz")
4461        target_tz = expression.args.get("target_tz")
4462        timestamp = expression.args.get("timestamp")
4463
4464        if source_tz and timestamp:
4465            timestamp = exp.AtTimeZone(
4466                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
4467            )
4468
4469        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
4470
4471        return self.sql(expr)
4472
4473    def json_sql(self, expression: exp.JSON) -> str:
4474        this = self.sql(expression, "this")
4475        this = f" {this}" if this else ""
4476
4477        _with = expression.args.get("with")
4478
4479        if _with is None:
4480            with_sql = ""
4481        elif not _with:
4482            with_sql = " WITHOUT"
4483        else:
4484            with_sql = " WITH"
4485
4486        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4487
4488        return f"JSON{this}{with_sql}{unique_sql}"
4489
4490    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
4491        def _generate_on_options(arg: t.Any) -> str:
4492            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
4493
4494        path = self.sql(expression, "path")
4495        returning = self.sql(expression, "returning")
4496        returning = f" RETURNING {returning}" if returning else ""
4497
4498        on_condition = self.sql(expression, "on_condition")
4499        on_condition = f" {on_condition}" if on_condition else ""
4500
4501        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
4502
4503    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
4504        else_ = "ELSE " if expression.args.get("else_") else ""
4505        condition = self.sql(expression, "expression")
4506        condition = f"WHEN {condition} THEN " if condition else else_
4507        insert = self.sql(expression, "this")[len("INSERT") :].strip()
4508        return f"{condition}{insert}"
4509
4510    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
4511        kind = self.sql(expression, "kind")
4512        expressions = self.seg(self.expressions(expression, sep=" "))
4513        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
4514        return res
4515
4516    def oncondition_sql(self, expression: exp.OnCondition) -> str:
4517        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
4518        empty = expression.args.get("empty")
4519        empty = (
4520            f"DEFAULT {empty} ON EMPTY"
4521            if isinstance(empty, exp.Expression)
4522            else self.sql(expression, "empty")
4523        )
4524
4525        error = expression.args.get("error")
4526        error = (
4527            f"DEFAULT {error} ON ERROR"
4528            if isinstance(error, exp.Expression)
4529            else self.sql(expression, "error")
4530        )
4531
4532        if error and empty:
4533            error = (
4534                f"{empty} {error}"
4535                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
4536                else f"{error} {empty}"
4537            )
4538            empty = ""
4539
4540        null = self.sql(expression, "null")
4541
4542        return f"{empty}{error}{null}"
4543
4544    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
4545        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
4546        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
4547
4548    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
4549        this = self.sql(expression, "this")
4550        path = self.sql(expression, "path")
4551
4552        passing = self.expressions(expression, "passing")
4553        passing = f" PASSING {passing}" if passing else ""
4554
4555        on_condition = self.sql(expression, "on_condition")
4556        on_condition = f" {on_condition}" if on_condition else ""
4557
4558        path = f"{path}{passing}{on_condition}"
4559
4560        return self.func("JSON_EXISTS", this, path)
4561
4562    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
4563        array_agg = self.function_fallback_sql(expression)
4564
4565        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
4566        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
4567        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
4568            parent = expression.parent
4569            if isinstance(parent, exp.Filter):
4570                parent_cond = parent.expression.this
4571                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
4572            else:
4573                this = expression.this
4574                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
4575                if this.find(exp.Column):
4576                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
4577                    this_sql = (
4578                        self.expressions(this)
4579                        if isinstance(this, exp.Distinct)
4580                        else self.sql(expression, "this")
4581                    )
4582
4583                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
4584
4585        return array_agg
4586
4587    def apply_sql(self, expression: exp.Apply) -> str:
4588        this = self.sql(expression, "this")
4589        expr = self.sql(expression, "expression")
4590
4591        return f"{this} APPLY({expr})"
4592
4593    def grant_sql(self, expression: exp.Grant) -> str:
4594        privileges_sql = self.expressions(expression, key="privileges", flat=True)
4595
4596        kind = self.sql(expression, "kind")
4597        kind = f" {kind}" if kind else ""
4598
4599        securable = self.sql(expression, "securable")
4600        securable = f" {securable}" if securable else ""
4601
4602        principals = self.expressions(expression, key="principals", flat=True)
4603
4604        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
4605
4606        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
4607
4608    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
4609        this = self.sql(expression, "this")
4610        columns = self.expressions(expression, flat=True)
4611        columns = f"({columns})" if columns else ""
4612
4613        return f"{this}{columns}"
4614
4615    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
4616        this = self.sql(expression, "this")
4617
4618        kind = self.sql(expression, "kind")
4619        kind = f"{kind} " if kind else ""
4620
4621        return f"{kind}{this}"
4622
4623    def columns_sql(self, expression: exp.Columns):
4624        func = self.function_fallback_sql(expression)
4625        if expression.args.get("unpack"):
4626            func = f"*{func}"
4627
4628        return func
4629
4630    def overlay_sql(self, expression: exp.Overlay):
4631        this = self.sql(expression, "this")
4632        expr = self.sql(expression, "expression")
4633        from_sql = self.sql(expression, "from")
4634        for_sql = self.sql(expression, "for")
4635        for_sql = f" FOR {for_sql}" if for_sql else ""
4636
4637        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
4638
4639    @unsupported_args("format")
4640    def todouble_sql(self, expression: exp.ToDouble) -> str:
4641        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4642
4643    def string_sql(self, expression: exp.String) -> str:
4644        this = expression.this
4645        zone = expression.args.get("zone")
4646
4647        if zone:
4648            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
4649            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
4650            # set for source_tz to transpile the time conversion before the STRING cast
4651            this = exp.ConvertTimezone(
4652                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
4653            )
4654
4655        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
4656
4657    def median_sql(self, expression: exp.Median):
4658        if not self.SUPPORTS_MEDIAN:
4659            return self.sql(
4660                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
4661            )
4662
4663        return self.function_fallback_sql(expression)
4664
4665    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
4666        filler = self.sql(expression, "this")
4667        filler = f" {filler}" if filler else ""
4668        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
4669        return f"TRUNCATE{filler} {with_count}"
4670
4671    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
4672        if self.SUPPORTS_UNIX_SECONDS:
4673            return self.function_fallback_sql(expression)
4674
4675        start_ts = exp.cast(
4676            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
4677        )
4678
4679        return self.sql(
4680            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
4681        )
4682
4683    def arraysize_sql(self, expression: exp.ArraySize) -> str:
4684        dim = expression.expression
4685
4686        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
4687        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
4688            if not (dim.is_int and dim.name == "1"):
4689                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
4690            dim = None
4691
4692        # If dimension is required but not specified, default initialize it
4693        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
4694            dim = exp.Literal.number(1)
4695
4696        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
4697
4698    def attach_sql(self, expression: exp.Attach) -> str:
4699        this = self.sql(expression, "this")
4700        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
4701        expressions = self.expressions(expression)
4702        expressions = f" ({expressions})" if expressions else ""
4703
4704        return f"ATTACH{exists_sql} {this}{expressions}"
4705
4706    def detach_sql(self, expression: exp.Detach) -> str:
4707        this = self.sql(expression, "this")
4708        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
4709
4710        return f"DETACH{exists_sql} {this}"
4711
4712    def attachoption_sql(self, expression: exp.AttachOption) -> str:
4713        this = self.sql(expression, "this")
4714        value = self.sql(expression, "expression")
4715        value = f" {value}" if value else ""
4716        return f"{this}{value}"
4717
4718    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
4719        this_sql = self.sql(expression, "this")
4720        if isinstance(expression.this, exp.Table):
4721            this_sql = f"TABLE {this_sql}"
4722
4723        return self.func(
4724            "FEATURES_AT_TIME",
4725            this_sql,
4726            expression.args.get("time"),
4727            expression.args.get("num_rows"),
4728            expression.args.get("ignore_feature_nulls"),
4729        )
4730
4731    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
4732        return (
4733            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
4734        )
4735
4736    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
4737        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
4738        encode = f"{encode} {self.sql(expression, 'this')}"
4739
4740        properties = expression.args.get("properties")
4741        if properties:
4742            encode = f"{encode} {self.properties(properties)}"
4743
4744        return encode
4745
4746    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
4747        this = self.sql(expression, "this")
4748        include = f"INCLUDE {this}"
4749
4750        column_def = self.sql(expression, "column_def")
4751        if column_def:
4752            include = f"{include} {column_def}"
4753
4754        alias = self.sql(expression, "alias")
4755        if alias:
4756            include = f"{include} AS {alias}"
4757
4758        return include
4759
4760    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
4761        name = f"NAME {self.sql(expression, 'this')}"
4762        return self.func("XMLELEMENT", name, *expression.expressions)
4763
4764    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
4765        partitions = self.expressions(expression, "partition_expressions")
4766        create = self.expressions(expression, "create_expressions")
4767        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
4768
4769    def partitionbyrangepropertydynamic_sql(
4770        self, expression: exp.PartitionByRangePropertyDynamic
4771    ) -> str:
4772        start = self.sql(expression, "start")
4773        end = self.sql(expression, "end")
4774
4775        every = expression.args["every"]
4776        if isinstance(every, exp.Interval) and every.this.is_string:
4777            every.this.replace(exp.Literal.number(every.name))
4778
4779        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
4780
4781    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
4782        name = self.sql(expression, "this")
4783        values = self.expressions(expression, flat=True)
4784
4785        return f"NAME {name} VALUE {values}"
4786
4787    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
4788        kind = self.sql(expression, "kind")
4789        sample = self.sql(expression, "sample")
4790        return f"SAMPLE {sample} {kind}"
4791
4792    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
4793        kind = self.sql(expression, "kind")
4794        option = self.sql(expression, "option")
4795        option = f" {option}" if option else ""
4796        this = self.sql(expression, "this")
4797        this = f" {this}" if this else ""
4798        columns = self.expressions(expression)
4799        columns = f" {columns}" if columns else ""
4800        return f"{kind}{option} STATISTICS{this}{columns}"
4801
4802    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
4803        this = self.sql(expression, "this")
4804        columns = self.expressions(expression)
4805        inner_expression = self.sql(expression, "expression")
4806        inner_expression = f" {inner_expression}" if inner_expression else ""
4807        update_options = self.sql(expression, "update_options")
4808        update_options = f" {update_options} UPDATE" if update_options else ""
4809        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
4810
4811    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
4812        kind = self.sql(expression, "kind")
4813        kind = f" {kind}" if kind else ""
4814        return f"DELETE{kind} STATISTICS"
4815
4816    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
4817        inner_expression = self.sql(expression, "expression")
4818        return f"LIST CHAINED ROWS{inner_expression}"
4819
4820    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
4821        kind = self.sql(expression, "kind")
4822        this = self.sql(expression, "this")
4823        this = f" {this}" if this else ""
4824        inner_expression = self.sql(expression, "expression")
4825        return f"VALIDATE {kind}{this}{inner_expression}"
4826
4827    def analyze_sql(self, expression: exp.Analyze) -> str:
4828        options = self.expressions(expression, key="options", sep=" ")
4829        options = f" {options}" if options else ""
4830        kind = self.sql(expression, "kind")
4831        kind = f" {kind}" if kind else ""
4832        this = self.sql(expression, "this")
4833        this = f" {this}" if this else ""
4834        mode = self.sql(expression, "mode")
4835        mode = f" {mode}" if mode else ""
4836        properties = self.sql(expression, "properties")
4837        properties = f" {properties}" if properties else ""
4838        partition = self.sql(expression, "partition")
4839        partition = f" {partition}" if partition else ""
4840        inner_expression = self.sql(expression, "expression")
4841        inner_expression = f" {inner_expression}" if inner_expression else ""
4842        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
4843
4844    def xmltable_sql(self, expression: exp.XMLTable) -> str:
4845        this = self.sql(expression, "this")
4846        namespaces = self.expressions(expression, key="namespaces")
4847        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
4848        passing = self.expressions(expression, key="passing")
4849        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
4850        columns = self.expressions(expression, key="columns")
4851        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
4852        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
4853        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
4854
4855    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
4856        this = self.sql(expression, "this")
4857        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
4858
4859    def export_sql(self, expression: exp.Export) -> str:
4860        this = self.sql(expression, "this")
4861        connection = self.sql(expression, "connection")
4862        connection = f"WITH CONNECTION {connection} " if connection else ""
4863        options = self.sql(expression, "options")
4864        return f"EXPORT DATA {connection}{options} AS {this}"
4865
4866    def declare_sql(self, expression: exp.Declare) -> str:
4867        return f"DECLARE {self.expressions(expression, flat=True)}"
4868
4869    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
4870        variable = self.sql(expression, "this")
4871        default = self.sql(expression, "default")
4872        default = f" = {default}" if default else ""
4873
4874        kind = self.sql(expression, "kind")
4875        if isinstance(expression.args.get("kind"), exp.Schema):
4876            kind = f"TABLE {kind}"
4877
4878        return f"{variable} AS {kind}{default}"
4879
4880    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
4881        kind = self.sql(expression, "kind")
4882        this = self.sql(expression, "this")
4883        set = self.sql(expression, "expression")
4884        using = self.sql(expression, "using")
4885        using = f" USING {using}" if using else ""
4886
4887        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
4888
4889        return f"{kind_sql} {this} SET {set}{using}"
4890
4891    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
4892        params = self.expressions(expression, key="params", flat=True)
4893        return self.func(expression.name, *expression.expressions) + f"({params})"
4894
4895    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
4896        return self.func(expression.name, *expression.expressions)
4897
4898    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
4899        return self.anonymousaggfunc_sql(expression)
4900
4901    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
4902        return self.parameterizedagg_sql(expression)
4903
4904    def show_sql(self, expression: exp.Show) -> str:
4905        self.unsupported("Unsupported SHOW statement")
4906        return ""
4907
4908    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
4909        # Snowflake GET/PUT statements:
4910        #   PUT <file> <internalStage> <properties>
4911        #   GET <internalStage> <file> <properties>
4912        props = expression.args.get("properties")
4913        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
4914        this = self.sql(expression, "this")
4915        target = self.sql(expression, "target")
4916
4917        if isinstance(expression, exp.Put):
4918            return f"PUT {this} {target}{props_sql}"
4919        else:
4920            return f"GET {target} {this}{props_sql}"
logger = <Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE = re.compile('\\\\(\\d+)')
UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
def unsupported_args( *args: Union[str, Tuple[str, str]]) -> Callable[[Callable[[~G, ~E], str]], Callable[[~G, ~E], str]]:
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:
  75class Generator(metaclass=_Generator):
  76    """
  77    Generator converts a given syntax tree to the corresponding SQL string.
  78
  79    Args:
  80        pretty: Whether to format the produced SQL string.
  81            Default: False.
  82        identify: Determines when an identifier should be quoted. Possible values are:
  83            False (default): Never quote, except in cases where it's mandatory by the dialect.
  84            True or 'always': Always quote.
  85            'safe': Only quote identifiers that are case insensitive.
  86        normalize: Whether to normalize identifiers to lowercase.
  87            Default: False.
  88        pad: The pad size in a formatted string. For example, this affects the indentation of
  89            a projection in a query, relative to its nesting level.
  90            Default: 2.
  91        indent: The indentation size in a formatted string. For example, this affects the
  92            indentation of subqueries and filters under a `WHERE` clause.
  93            Default: 2.
  94        normalize_functions: How to normalize function names. Possible values are:
  95            "upper" or True (default): Convert names to uppercase.
  96            "lower": Convert names to lowercase.
  97            False: Disables function name normalization.
  98        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
  99            Default ErrorLevel.WARN.
 100        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
 101            This is only relevant if unsupported_level is ErrorLevel.RAISE.
 102            Default: 3
 103        leading_comma: Whether the comma is leading or trailing in select expressions.
 104            This is only relevant when generating in pretty mode.
 105            Default: False
 106        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
 107            The default is on the smaller end because the length only represents a segment and not the true
 108            line length.
 109            Default: 80
 110        comments: Whether to preserve comments in the output SQL code.
 111            Default: True
 112    """
 113
 114    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
 115        **JSON_PATH_PART_TRANSFORMS,
 116        exp.AllowedValuesProperty: lambda self,
 117        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
 118        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
 119        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
 120        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
 121        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
 122        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
 123        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
 124        exp.CaseSpecificColumnConstraint: lambda _,
 125        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
 126        exp.Ceil: lambda self, e: self.ceil_floor(e),
 127        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
 128        exp.CharacterSetProperty: lambda self,
 129        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
 130        exp.ClusteredColumnConstraint: lambda self,
 131        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
 132        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
 133        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
 134        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
 135        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
 136        exp.CredentialsProperty: lambda self,
 137        e: f"CREDENTIALS=({self.expressions(e, 'expressions', sep=' ')})",
 138        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
 139        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
 140        exp.DynamicProperty: lambda *_: "DYNAMIC",
 141        exp.EmptyProperty: lambda *_: "EMPTY",
 142        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
 143        exp.EphemeralColumnConstraint: lambda self,
 144        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
 145        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
 146        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
 147        exp.Except: lambda self, e: self.set_operations(e),
 148        exp.ExternalProperty: lambda *_: "EXTERNAL",
 149        exp.Floor: lambda self, e: self.ceil_floor(e),
 150        exp.Get: lambda self, e: self.get_put_sql(e),
 151        exp.GlobalProperty: lambda *_: "GLOBAL",
 152        exp.HeapProperty: lambda *_: "HEAP",
 153        exp.IcebergProperty: lambda *_: "ICEBERG",
 154        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
 155        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
 156        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
 157        exp.Intersect: lambda self, e: self.set_operations(e),
 158        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
 159        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
 160        exp.LanguageProperty: lambda self, e: self.naked_property(e),
 161        exp.LocationProperty: lambda self, e: self.naked_property(e),
 162        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 163        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
 164        exp.NonClusteredColumnConstraint: lambda self,
 165        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 166        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
 167        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
 168        exp.OnCommitProperty: lambda _,
 169        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
 170        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
 171        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
 172        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
 173        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
 174        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
 175        exp.PartitionedByBucket: lambda self, e: self.func("BUCKET", e.this, e.expression),
 176        exp.PartitionByTruncate: lambda self, e: self.func("TRUNCATE", e.this, e.expression),
 177        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
 178        exp.ProjectionPolicyColumnConstraint: lambda self,
 179        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
 180        exp.Put: lambda self, e: self.get_put_sql(e),
 181        exp.RemoteWithConnectionModelProperty: lambda self,
 182        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
 183        exp.ReturnsProperty: lambda self, e: (
 184            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
 185        ),
 186        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
 187        exp.SecureProperty: lambda *_: "SECURE",
 188        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
 189        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
 190        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
 191        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
 192        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
 193        exp.SqlReadWriteProperty: lambda _, e: e.name,
 194        exp.SqlSecurityProperty: lambda _,
 195        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 196        exp.StabilityProperty: lambda _, e: e.name,
 197        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
 198        exp.StreamingTableProperty: lambda *_: "STREAMING",
 199        exp.StrictProperty: lambda *_: "STRICT",
 200        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
 201        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
 202        exp.TemporaryProperty: lambda *_: "TEMPORARY",
 203        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
 204        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
 205        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
 206        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
 207        exp.TransientProperty: lambda *_: "TRANSIENT",
 208        exp.Union: lambda self, e: self.set_operations(e),
 209        exp.UnloggedProperty: lambda *_: "UNLOGGED",
 210        exp.UsingTemplateProperty: lambda self, e: f"USING TEMPLATE {self.sql(e, 'this')}",
 211        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
 212        exp.Uuid: lambda *_: "UUID()",
 213        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
 214        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 215        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
 216        exp.VolatileProperty: lambda *_: "VOLATILE",
 217        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
 218        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
 219        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
 220        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
 221        exp.ForceProperty: lambda *_: "FORCE",
 222    }
 223
 224    # Whether null ordering is supported in order by
 225    # True: Full Support, None: No support, False: No support for certain cases
 226    # such as window specifications, aggregate functions etc
 227    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
 228
 229    # Whether ignore nulls is inside the agg or outside.
 230    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
 231    IGNORE_NULLS_IN_FUNC = False
 232
 233    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 234    LOCKING_READS_SUPPORTED = False
 235
 236    # Whether the EXCEPT and INTERSECT operations can return duplicates
 237    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
 238
 239    # Wrap derived values in parens, usually standard but spark doesn't support it
 240    WRAP_DERIVED_VALUES = True
 241
 242    # Whether create function uses an AS before the RETURN
 243    CREATE_FUNCTION_RETURN_AS = True
 244
 245    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
 246    MATCHED_BY_SOURCE = True
 247
 248    # Whether the INTERVAL expression works only with values like '1 day'
 249    SINGLE_STRING_INTERVAL = False
 250
 251    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
 252    INTERVAL_ALLOWS_PLURAL_FORM = True
 253
 254    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
 255    LIMIT_FETCH = "ALL"
 256
 257    # Whether limit and fetch allows expresions or just limits
 258    LIMIT_ONLY_LITERALS = False
 259
 260    # Whether a table is allowed to be renamed with a db
 261    RENAME_TABLE_WITH_DB = True
 262
 263    # The separator for grouping sets and rollups
 264    GROUPINGS_SEP = ","
 265
 266    # The string used for creating an index on a table
 267    INDEX_ON = "ON"
 268
 269    # Whether join hints should be generated
 270    JOIN_HINTS = True
 271
 272    # Whether table hints should be generated
 273    TABLE_HINTS = True
 274
 275    # Whether query hints should be generated
 276    QUERY_HINTS = True
 277
 278    # What kind of separator to use for query hints
 279    QUERY_HINT_SEP = ", "
 280
 281    # Whether comparing against booleans (e.g. x IS TRUE) is supported
 282    IS_BOOL_ALLOWED = True
 283
 284    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
 285    DUPLICATE_KEY_UPDATE_WITH_SET = True
 286
 287    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
 288    LIMIT_IS_TOP = False
 289
 290    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
 291    RETURNING_END = True
 292
 293    # Whether to generate an unquoted value for EXTRACT's date part argument
 294    EXTRACT_ALLOWS_QUOTES = True
 295
 296    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
 297    TZ_TO_WITH_TIME_ZONE = False
 298
 299    # Whether the NVL2 function is supported
 300    NVL2_SUPPORTED = True
 301
 302    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
 303    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
 304
 305    # Whether VALUES statements can be used as derived tables.
 306    # MySQL 5 and Redshift do not allow this, so when False, it will convert
 307    # SELECT * VALUES into SELECT UNION
 308    VALUES_AS_TABLE = True
 309
 310    # Whether the word COLUMN is included when adding a column with ALTER TABLE
 311    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
 312
 313    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
 314    UNNEST_WITH_ORDINALITY = True
 315
 316    # Whether FILTER (WHERE cond) can be used for conditional aggregation
 317    AGGREGATE_FILTER_SUPPORTED = True
 318
 319    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
 320    SEMI_ANTI_JOIN_WITH_SIDE = True
 321
 322    # Whether to include the type of a computed column in the CREATE DDL
 323    COMPUTED_COLUMN_WITH_TYPE = True
 324
 325    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
 326    SUPPORTS_TABLE_COPY = True
 327
 328    # Whether parentheses are required around the table sample's expression
 329    TABLESAMPLE_REQUIRES_PARENS = True
 330
 331    # Whether a table sample clause's size needs to be followed by the ROWS keyword
 332    TABLESAMPLE_SIZE_IS_ROWS = True
 333
 334    # The keyword(s) to use when generating a sample clause
 335    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
 336
 337    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
 338    TABLESAMPLE_WITH_METHOD = True
 339
 340    # The keyword to use when specifying the seed of a sample clause
 341    TABLESAMPLE_SEED_KEYWORD = "SEED"
 342
 343    # Whether COLLATE is a function instead of a binary operator
 344    COLLATE_IS_FUNC = False
 345
 346    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
 347    DATA_TYPE_SPECIFIERS_ALLOWED = False
 348
 349    # Whether conditions require booleans WHERE x = 0 vs WHERE x
 350    ENSURE_BOOLS = False
 351
 352    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
 353    CTE_RECURSIVE_KEYWORD_REQUIRED = True
 354
 355    # Whether CONCAT requires >1 arguments
 356    SUPPORTS_SINGLE_ARG_CONCAT = True
 357
 358    # Whether LAST_DAY function supports a date part argument
 359    LAST_DAY_SUPPORTS_DATE_PART = True
 360
 361    # Whether named columns are allowed in table aliases
 362    SUPPORTS_TABLE_ALIAS_COLUMNS = True
 363
 364    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
 365    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
 366
 367    # What delimiter to use for separating JSON key/value pairs
 368    JSON_KEY_VALUE_PAIR_SEP = ":"
 369
 370    # INSERT OVERWRITE TABLE x override
 371    INSERT_OVERWRITE = " OVERWRITE TABLE"
 372
 373    # Whether the SELECT .. INTO syntax is used instead of CTAS
 374    SUPPORTS_SELECT_INTO = False
 375
 376    # Whether UNLOGGED tables can be created
 377    SUPPORTS_UNLOGGED_TABLES = False
 378
 379    # Whether the CREATE TABLE LIKE statement is supported
 380    SUPPORTS_CREATE_TABLE_LIKE = True
 381
 382    # Whether the LikeProperty needs to be specified inside of the schema clause
 383    LIKE_PROPERTY_INSIDE_SCHEMA = False
 384
 385    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
 386    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
 387    MULTI_ARG_DISTINCT = True
 388
 389    # Whether the JSON extraction operators expect a value of type JSON
 390    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
 391
 392    # Whether bracketed keys like ["foo"] are supported in JSON paths
 393    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
 394
 395    # Whether to escape keys using single quotes in JSON paths
 396    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
 397
 398    # The JSONPathPart expressions supported by this dialect
 399    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
 400
 401    # Whether any(f(x) for x in array) can be implemented by this dialect
 402    CAN_IMPLEMENT_ARRAY_ANY = False
 403
 404    # Whether the function TO_NUMBER is supported
 405    SUPPORTS_TO_NUMBER = True
 406
 407    # Whether or not set op modifiers apply to the outer set op or select.
 408    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
 409    # True means limit 1 happens after the set op, False means it it happens on y.
 410    SET_OP_MODIFIERS = True
 411
 412    # Whether parameters from COPY statement are wrapped in parentheses
 413    COPY_PARAMS_ARE_WRAPPED = True
 414
 415    # Whether values of params are set with "=" token or empty space
 416    COPY_PARAMS_EQ_REQUIRED = False
 417
 418    # Whether COPY statement has INTO keyword
 419    COPY_HAS_INTO_KEYWORD = True
 420
 421    # Whether the conditional TRY(expression) function is supported
 422    TRY_SUPPORTED = True
 423
 424    # Whether the UESCAPE syntax in unicode strings is supported
 425    SUPPORTS_UESCAPE = True
 426
 427    # The keyword to use when generating a star projection with excluded columns
 428    STAR_EXCEPT = "EXCEPT"
 429
 430    # The HEX function name
 431    HEX_FUNC = "HEX"
 432
 433    # The keywords to use when prefixing & separating WITH based properties
 434    WITH_PROPERTIES_PREFIX = "WITH"
 435
 436    # Whether to quote the generated expression of exp.JsonPath
 437    QUOTE_JSON_PATH = True
 438
 439    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
 440    PAD_FILL_PATTERN_IS_REQUIRED = False
 441
 442    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
 443    SUPPORTS_EXPLODING_PROJECTIONS = True
 444
 445    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
 446    ARRAY_CONCAT_IS_VAR_LEN = True
 447
 448    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
 449    SUPPORTS_CONVERT_TIMEZONE = False
 450
 451    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
 452    SUPPORTS_MEDIAN = True
 453
 454    # Whether UNIX_SECONDS(timestamp) is supported
 455    SUPPORTS_UNIX_SECONDS = False
 456
 457    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
 458    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
 459
 460    # The function name of the exp.ArraySize expression
 461    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
 462
 463    # The syntax to use when altering the type of a column
 464    ALTER_SET_TYPE = "SET DATA TYPE"
 465
 466    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
 467    # None -> Doesn't support it at all
 468    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
 469    # True (Postgres) -> Explicitly requires it
 470    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
 471
 472    TYPE_MAPPING = {
 473        exp.DataType.Type.DATETIME2: "TIMESTAMP",
 474        exp.DataType.Type.NCHAR: "CHAR",
 475        exp.DataType.Type.NVARCHAR: "VARCHAR",
 476        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 477        exp.DataType.Type.LONGTEXT: "TEXT",
 478        exp.DataType.Type.TINYTEXT: "TEXT",
 479        exp.DataType.Type.BLOB: "VARBINARY",
 480        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 481        exp.DataType.Type.LONGBLOB: "BLOB",
 482        exp.DataType.Type.TINYBLOB: "BLOB",
 483        exp.DataType.Type.INET: "INET",
 484        exp.DataType.Type.ROWVERSION: "VARBINARY",
 485        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
 486    }
 487
 488    TIME_PART_SINGULARS = {
 489        "MICROSECONDS": "MICROSECOND",
 490        "SECONDS": "SECOND",
 491        "MINUTES": "MINUTE",
 492        "HOURS": "HOUR",
 493        "DAYS": "DAY",
 494        "WEEKS": "WEEK",
 495        "MONTHS": "MONTH",
 496        "QUARTERS": "QUARTER",
 497        "YEARS": "YEAR",
 498    }
 499
 500    AFTER_HAVING_MODIFIER_TRANSFORMS = {
 501        "cluster": lambda self, e: self.sql(e, "cluster"),
 502        "distribute": lambda self, e: self.sql(e, "distribute"),
 503        "sort": lambda self, e: self.sql(e, "sort"),
 504        "windows": lambda self, e: (
 505            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
 506            if e.args.get("windows")
 507            else ""
 508        ),
 509        "qualify": lambda self, e: self.sql(e, "qualify"),
 510    }
 511
 512    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 513
 514    STRUCT_DELIMITER = ("<", ">")
 515
 516    PARAMETER_TOKEN = "@"
 517    NAMED_PLACEHOLDER_TOKEN = ":"
 518
 519    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
 520
 521    PROPERTIES_LOCATION = {
 522        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
 523        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 524        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 525        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
 526        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
 527        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 528        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 529        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 530        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 531        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
 532        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 533        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
 534        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
 535        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
 536        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 537        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
 538        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 539        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
 540        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
 541        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
 542        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 543        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 544        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
 545        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
 546        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 547        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 548        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 549        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 550        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 551        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 552        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
 553        exp.HeapProperty: exp.Properties.Location.POST_WITH,
 554        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
 555        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
 556        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
 557        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
 558        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 559        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 560        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 561        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 562        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 563        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
 564        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 565        exp.LogProperty: exp.Properties.Location.POST_NAME,
 566        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 567        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 568        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 569        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 570        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 571        exp.Order: exp.Properties.Location.POST_SCHEMA,
 572        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 573        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 574        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 575        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 576        exp.Property: exp.Properties.Location.POST_WITH,
 577        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 578        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 579        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 580        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 581        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 582        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 583        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 584        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
 585        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
 586        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 587        exp.Set: exp.Properties.Location.POST_SCHEMA,
 588        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 589        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 590        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 591        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
 592        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
 593        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 594        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 595        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 596        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 597        exp.StorageHandlerProperty: exp.Properties.Location.POST_SCHEMA,
 598        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
 599        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
 600        exp.Tags: exp.Properties.Location.POST_WITH,
 601        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 602        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 603        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 604        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 605        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 606        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
 607        exp.UsingTemplateProperty: exp.Properties.Location.POST_SCHEMA,
 608        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
 609        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 610        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 611        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 612        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
 613        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
 614        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 615        exp.ForceProperty: exp.Properties.Location.POST_CREATE,
 616    }
 617
 618    # Keywords that can't be used as unquoted identifier names
 619    RESERVED_KEYWORDS: t.Set[str] = set()
 620
 621    # Expressions whose comments are separated from them for better formatting
 622    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 623        exp.Command,
 624        exp.Create,
 625        exp.Describe,
 626        exp.Delete,
 627        exp.Drop,
 628        exp.From,
 629        exp.Insert,
 630        exp.Join,
 631        exp.MultitableInserts,
 632        exp.Select,
 633        exp.SetOperation,
 634        exp.Update,
 635        exp.Where,
 636        exp.With,
 637    )
 638
 639    # Expressions that should not have their comments generated in maybe_comment
 640    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 641        exp.Binary,
 642        exp.SetOperation,
 643    )
 644
 645    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 646    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 647        exp.Column,
 648        exp.Literal,
 649        exp.Neg,
 650        exp.Paren,
 651    )
 652
 653    PARAMETERIZABLE_TEXT_TYPES = {
 654        exp.DataType.Type.NVARCHAR,
 655        exp.DataType.Type.VARCHAR,
 656        exp.DataType.Type.CHAR,
 657        exp.DataType.Type.NCHAR,
 658    }
 659
 660    # Expressions that need to have all CTEs under them bubbled up to them
 661    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 662
 663    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 664
 665    __slots__ = (
 666        "pretty",
 667        "identify",
 668        "normalize",
 669        "pad",
 670        "_indent",
 671        "normalize_functions",
 672        "unsupported_level",
 673        "max_unsupported",
 674        "leading_comma",
 675        "max_text_width",
 676        "comments",
 677        "dialect",
 678        "unsupported_messages",
 679        "_escaped_quote_end",
 680        "_escaped_identifier_end",
 681        "_next_name",
 682        "_identifier_start",
 683        "_identifier_end",
 684        "_quote_json_path_key_using_brackets",
 685    )
 686
 687    def __init__(
 688        self,
 689        pretty: t.Optional[bool] = None,
 690        identify: str | bool = False,
 691        normalize: bool = False,
 692        pad: int = 2,
 693        indent: int = 2,
 694        normalize_functions: t.Optional[str | bool] = None,
 695        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 696        max_unsupported: int = 3,
 697        leading_comma: bool = False,
 698        max_text_width: int = 80,
 699        comments: bool = True,
 700        dialect: DialectType = None,
 701    ):
 702        import sqlglot
 703        from sqlglot.dialects import Dialect
 704
 705        self.pretty = pretty if pretty is not None else sqlglot.pretty
 706        self.identify = identify
 707        self.normalize = normalize
 708        self.pad = pad
 709        self._indent = indent
 710        self.unsupported_level = unsupported_level
 711        self.max_unsupported = max_unsupported
 712        self.leading_comma = leading_comma
 713        self.max_text_width = max_text_width
 714        self.comments = comments
 715        self.dialect = Dialect.get_or_raise(dialect)
 716
 717        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 718        self.normalize_functions = (
 719            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 720        )
 721
 722        self.unsupported_messages: t.List[str] = []
 723        self._escaped_quote_end: str = (
 724            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 725        )
 726        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
 727
 728        self._next_name = name_sequence("_t")
 729
 730        self._identifier_start = self.dialect.IDENTIFIER_START
 731        self._identifier_end = self.dialect.IDENTIFIER_END
 732
 733        self._quote_json_path_key_using_brackets = True
 734
 735    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 736        """
 737        Generates the SQL string corresponding to the given syntax tree.
 738
 739        Args:
 740            expression: The syntax tree.
 741            copy: Whether to copy the expression. The generator performs mutations so
 742                it is safer to copy.
 743
 744        Returns:
 745            The SQL string corresponding to `expression`.
 746        """
 747        if copy:
 748            expression = expression.copy()
 749
 750        expression = self.preprocess(expression)
 751
 752        self.unsupported_messages = []
 753        sql = self.sql(expression).strip()
 754
 755        if self.pretty:
 756            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 757
 758        if self.unsupported_level == ErrorLevel.IGNORE:
 759            return sql
 760
 761        if self.unsupported_level == ErrorLevel.WARN:
 762            for msg in self.unsupported_messages:
 763                logger.warning(msg)
 764        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 765            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 766
 767        return sql
 768
 769    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 770        """Apply generic preprocessing transformations to a given expression."""
 771        expression = self._move_ctes_to_top_level(expression)
 772
 773        if self.ENSURE_BOOLS:
 774            from sqlglot.transforms import ensure_bools
 775
 776            expression = ensure_bools(expression)
 777
 778        return expression
 779
 780    def _move_ctes_to_top_level(self, expression: E) -> E:
 781        if (
 782            not expression.parent
 783            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 784            and any(node.parent is not expression for node in expression.find_all(exp.With))
 785        ):
 786            from sqlglot.transforms import move_ctes_to_top_level
 787
 788            expression = move_ctes_to_top_level(expression)
 789        return expression
 790
 791    def unsupported(self, message: str) -> None:
 792        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 793            raise UnsupportedError(message)
 794        self.unsupported_messages.append(message)
 795
 796    def sep(self, sep: str = " ") -> str:
 797        return f"{sep.strip()}\n" if self.pretty else sep
 798
 799    def seg(self, sql: str, sep: str = " ") -> str:
 800        return f"{self.sep(sep)}{sql}"
 801
 802    def pad_comment(self, comment: str) -> str:
 803        comment = " " + comment if comment[0].strip() else comment
 804        comment = comment + " " if comment[-1].strip() else comment
 805        return comment
 806
 807    def maybe_comment(
 808        self,
 809        sql: str,
 810        expression: t.Optional[exp.Expression] = None,
 811        comments: t.Optional[t.List[str]] = None,
 812        separated: bool = False,
 813    ) -> str:
 814        comments = (
 815            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 816            if self.comments
 817            else None
 818        )
 819
 820        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 821            return sql
 822
 823        comments_sql = " ".join(
 824            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 825        )
 826
 827        if not comments_sql:
 828            return sql
 829
 830        comments_sql = self._replace_line_breaks(comments_sql)
 831
 832        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 833            return (
 834                f"{self.sep()}{comments_sql}{sql}"
 835                if not sql or sql[0].isspace()
 836                else f"{comments_sql}{self.sep()}{sql}"
 837            )
 838
 839        return f"{sql} {comments_sql}"
 840
 841    def wrap(self, expression: exp.Expression | str) -> str:
 842        this_sql = (
 843            self.sql(expression)
 844            if isinstance(expression, exp.UNWRAPPED_QUERIES)
 845            else self.sql(expression, "this")
 846        )
 847        if not this_sql:
 848            return "()"
 849
 850        this_sql = self.indent(this_sql, level=1, pad=0)
 851        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 852
 853    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 854        original = self.identify
 855        self.identify = False
 856        result = func(*args, **kwargs)
 857        self.identify = original
 858        return result
 859
 860    def normalize_func(self, name: str) -> str:
 861        if self.normalize_functions == "upper" or self.normalize_functions is True:
 862            return name.upper()
 863        if self.normalize_functions == "lower":
 864            return name.lower()
 865        return name
 866
 867    def indent(
 868        self,
 869        sql: str,
 870        level: int = 0,
 871        pad: t.Optional[int] = None,
 872        skip_first: bool = False,
 873        skip_last: bool = False,
 874    ) -> str:
 875        if not self.pretty or not sql:
 876            return sql
 877
 878        pad = self.pad if pad is None else pad
 879        lines = sql.split("\n")
 880
 881        return "\n".join(
 882            (
 883                line
 884                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 885                else f"{' ' * (level * self._indent + pad)}{line}"
 886            )
 887            for i, line in enumerate(lines)
 888        )
 889
 890    def sql(
 891        self,
 892        expression: t.Optional[str | exp.Expression],
 893        key: t.Optional[str] = None,
 894        comment: bool = True,
 895    ) -> str:
 896        if not expression:
 897            return ""
 898
 899        if isinstance(expression, str):
 900            return expression
 901
 902        if key:
 903            value = expression.args.get(key)
 904            if value:
 905                return self.sql(value)
 906            return ""
 907
 908        transform = self.TRANSFORMS.get(expression.__class__)
 909
 910        if callable(transform):
 911            sql = transform(self, expression)
 912        elif isinstance(expression, exp.Expression):
 913            exp_handler_name = f"{expression.key}_sql"
 914
 915            if hasattr(self, exp_handler_name):
 916                sql = getattr(self, exp_handler_name)(expression)
 917            elif isinstance(expression, exp.Func):
 918                sql = self.function_fallback_sql(expression)
 919            elif isinstance(expression, exp.Property):
 920                sql = self.property_sql(expression)
 921            else:
 922                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 923        else:
 924            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 925
 926        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 927
 928    def uncache_sql(self, expression: exp.Uncache) -> str:
 929        table = self.sql(expression, "this")
 930        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 931        return f"UNCACHE TABLE{exists_sql} {table}"
 932
 933    def cache_sql(self, expression: exp.Cache) -> str:
 934        lazy = " LAZY" if expression.args.get("lazy") else ""
 935        table = self.sql(expression, "this")
 936        options = expression.args.get("options")
 937        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 938        sql = self.sql(expression, "expression")
 939        sql = f" AS{self.sep()}{sql}" if sql else ""
 940        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 941        return self.prepend_ctes(expression, sql)
 942
 943    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 944        if isinstance(expression.parent, exp.Cast):
 945            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 946        default = "DEFAULT " if expression.args.get("default") else ""
 947        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 948
 949    def column_parts(self, expression: exp.Column) -> str:
 950        return ".".join(
 951            self.sql(part)
 952            for part in (
 953                expression.args.get("catalog"),
 954                expression.args.get("db"),
 955                expression.args.get("table"),
 956                expression.args.get("this"),
 957            )
 958            if part
 959        )
 960
 961    def column_sql(self, expression: exp.Column) -> str:
 962        join_mark = " (+)" if expression.args.get("join_mark") else ""
 963
 964        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
 965            join_mark = ""
 966            self.unsupported("Outer join syntax using the (+) operator is not supported.")
 967
 968        return f"{self.column_parts(expression)}{join_mark}"
 969
 970    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
 971        this = self.sql(expression, "this")
 972        this = f" {this}" if this else ""
 973        position = self.sql(expression, "position")
 974        return f"{position}{this}"
 975
 976    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
 977        column = self.sql(expression, "this")
 978        kind = self.sql(expression, "kind")
 979        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 980        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 981        kind = f"{sep}{kind}" if kind else ""
 982        constraints = f" {constraints}" if constraints else ""
 983        position = self.sql(expression, "position")
 984        position = f" {position}" if position else ""
 985
 986        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
 987            kind = ""
 988
 989        return f"{exists}{column}{kind}{constraints}{position}"
 990
 991    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 992        this = self.sql(expression, "this")
 993        kind_sql = self.sql(expression, "kind").strip()
 994        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 995
 996    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
 997        this = self.sql(expression, "this")
 998        if expression.args.get("not_null"):
 999            persisted = " PERSISTED NOT NULL"
1000        elif expression.args.get("persisted"):
1001            persisted = " PERSISTED"
1002        else:
1003            persisted = ""
1004        return f"AS {this}{persisted}"
1005
1006    def autoincrementcolumnconstraint_sql(self, _) -> str:
1007        return self.token_sql(TokenType.AUTO_INCREMENT)
1008
1009    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
1010        if isinstance(expression.this, list):
1011            this = self.wrap(self.expressions(expression, key="this", flat=True))
1012        else:
1013            this = self.sql(expression, "this")
1014
1015        return f"COMPRESS {this}"
1016
1017    def generatedasidentitycolumnconstraint_sql(
1018        self, expression: exp.GeneratedAsIdentityColumnConstraint
1019    ) -> str:
1020        this = ""
1021        if expression.this is not None:
1022            on_null = " ON NULL" if expression.args.get("on_null") else ""
1023            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
1024
1025        start = expression.args.get("start")
1026        start = f"START WITH {start}" if start else ""
1027        increment = expression.args.get("increment")
1028        increment = f" INCREMENT BY {increment}" if increment else ""
1029        minvalue = expression.args.get("minvalue")
1030        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1031        maxvalue = expression.args.get("maxvalue")
1032        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1033        cycle = expression.args.get("cycle")
1034        cycle_sql = ""
1035
1036        if cycle is not None:
1037            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
1038            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
1039
1040        sequence_opts = ""
1041        if start or increment or cycle_sql:
1042            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
1043            sequence_opts = f" ({sequence_opts.strip()})"
1044
1045        expr = self.sql(expression, "expression")
1046        expr = f"({expr})" if expr else "IDENTITY"
1047
1048        return f"GENERATED{this} AS {expr}{sequence_opts}"
1049
1050    def generatedasrowcolumnconstraint_sql(
1051        self, expression: exp.GeneratedAsRowColumnConstraint
1052    ) -> str:
1053        start = "START" if expression.args.get("start") else "END"
1054        hidden = " HIDDEN" if expression.args.get("hidden") else ""
1055        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
1056
1057    def periodforsystemtimeconstraint_sql(
1058        self, expression: exp.PeriodForSystemTimeConstraint
1059    ) -> str:
1060        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
1061
1062    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
1063        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
1064
1065    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
1066        return f"AS {self.sql(expression, 'this')}"
1067
1068    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
1069        desc = expression.args.get("desc")
1070        if desc is not None:
1071            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1072        options = self.expressions(expression, key="options", flat=True, sep=" ")
1073        options = f" {options}" if options else ""
1074        return f"PRIMARY KEY{options}"
1075
1076    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1077        this = self.sql(expression, "this")
1078        this = f" {this}" if this else ""
1079        index_type = expression.args.get("index_type")
1080        index_type = f" USING {index_type}" if index_type else ""
1081        on_conflict = self.sql(expression, "on_conflict")
1082        on_conflict = f" {on_conflict}" if on_conflict else ""
1083        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1084        options = self.expressions(expression, key="options", flat=True, sep=" ")
1085        options = f" {options}" if options else ""
1086        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
1087
1088    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
1089        return self.sql(expression, "this")
1090
1091    def create_sql(self, expression: exp.Create) -> str:
1092        kind = self.sql(expression, "kind")
1093        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1094        properties = expression.args.get("properties")
1095        properties_locs = self.locate_properties(properties) if properties else defaultdict()
1096
1097        this = self.createable_sql(expression, properties_locs)
1098
1099        properties_sql = ""
1100        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
1101            exp.Properties.Location.POST_WITH
1102        ):
1103            properties_sql = self.sql(
1104                exp.Properties(
1105                    expressions=[
1106                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
1107                        *properties_locs[exp.Properties.Location.POST_WITH],
1108                    ]
1109                )
1110            )
1111
1112            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
1113                properties_sql = self.sep() + properties_sql
1114            elif not self.pretty:
1115                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
1116                properties_sql = f" {properties_sql}"
1117
1118        begin = " BEGIN" if expression.args.get("begin") else ""
1119        end = " END" if expression.args.get("end") else ""
1120
1121        expression_sql = self.sql(expression, "expression")
1122        if expression_sql:
1123            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
1124
1125            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1126                postalias_props_sql = ""
1127                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1128                    postalias_props_sql = self.properties(
1129                        exp.Properties(
1130                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
1131                        ),
1132                        wrapped=False,
1133                    )
1134                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
1135                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
1136
1137        postindex_props_sql = ""
1138        if properties_locs.get(exp.Properties.Location.POST_INDEX):
1139            postindex_props_sql = self.properties(
1140                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
1141                wrapped=False,
1142                prefix=" ",
1143            )
1144
1145        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
1146        indexes = f" {indexes}" if indexes else ""
1147        index_sql = indexes + postindex_props_sql
1148
1149        replace = " OR REPLACE" if expression.args.get("replace") else ""
1150        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
1151        unique = " UNIQUE" if expression.args.get("unique") else ""
1152
1153        clustered = expression.args.get("clustered")
1154        if clustered is None:
1155            clustered_sql = ""
1156        elif clustered:
1157            clustered_sql = " CLUSTERED COLUMNSTORE"
1158        else:
1159            clustered_sql = " NONCLUSTERED COLUMNSTORE"
1160
1161        postcreate_props_sql = ""
1162        if properties_locs.get(exp.Properties.Location.POST_CREATE):
1163            postcreate_props_sql = self.properties(
1164                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
1165                sep=" ",
1166                prefix=" ",
1167                wrapped=False,
1168            )
1169
1170        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
1171
1172        postexpression_props_sql = ""
1173        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
1174            postexpression_props_sql = self.properties(
1175                exp.Properties(
1176                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
1177                ),
1178                sep=" ",
1179                prefix=" ",
1180                wrapped=False,
1181            )
1182
1183        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1184        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
1185        no_schema_binding = (
1186            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
1187        )
1188
1189        clone = self.sql(expression, "clone")
1190        clone = f" {clone}" if clone else ""
1191
1192        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
1193            properties_expression = f"{expression_sql}{properties_sql}"
1194        else:
1195            properties_expression = f"{properties_sql}{expression_sql}"
1196
1197        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
1198        return self.prepend_ctes(expression, expression_sql)
1199
1200    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
1201        start = self.sql(expression, "start")
1202        start = f"START WITH {start}" if start else ""
1203        increment = self.sql(expression, "increment")
1204        increment = f" INCREMENT BY {increment}" if increment else ""
1205        minvalue = self.sql(expression, "minvalue")
1206        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1207        maxvalue = self.sql(expression, "maxvalue")
1208        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1209        owned = self.sql(expression, "owned")
1210        owned = f" OWNED BY {owned}" if owned else ""
1211
1212        cache = expression.args.get("cache")
1213        if cache is None:
1214            cache_str = ""
1215        elif cache is True:
1216            cache_str = " CACHE"
1217        else:
1218            cache_str = f" CACHE {cache}"
1219
1220        options = self.expressions(expression, key="options", flat=True, sep=" ")
1221        options = f" {options}" if options else ""
1222
1223        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1224
1225    def clone_sql(self, expression: exp.Clone) -> str:
1226        this = self.sql(expression, "this")
1227        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1228        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1229        return f"{shallow}{keyword} {this}"
1230
1231    def describe_sql(self, expression: exp.Describe) -> str:
1232        style = expression.args.get("style")
1233        style = f" {style}" if style else ""
1234        partition = self.sql(expression, "partition")
1235        partition = f" {partition}" if partition else ""
1236        format = self.sql(expression, "format")
1237        format = f" {format}" if format else ""
1238
1239        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
1240
1241    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1242        tag = self.sql(expression, "tag")
1243        return f"${tag}${self.sql(expression, 'this')}${tag}$"
1244
1245    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1246        with_ = self.sql(expression, "with")
1247        if with_:
1248            sql = f"{with_}{self.sep()}{sql}"
1249        return sql
1250
1251    def with_sql(self, expression: exp.With) -> str:
1252        sql = self.expressions(expression, flat=True)
1253        recursive = (
1254            "RECURSIVE "
1255            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1256            else ""
1257        )
1258        search = self.sql(expression, "search")
1259        search = f" {search}" if search else ""
1260
1261        return f"WITH {recursive}{sql}{search}"
1262
1263    def cte_sql(self, expression: exp.CTE) -> str:
1264        alias = expression.args.get("alias")
1265        if alias:
1266            alias.add_comments(expression.pop_comments())
1267
1268        alias_sql = self.sql(expression, "alias")
1269
1270        materialized = expression.args.get("materialized")
1271        if materialized is False:
1272            materialized = "NOT MATERIALIZED "
1273        elif materialized:
1274            materialized = "MATERIALIZED "
1275
1276        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
1277
1278    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1279        alias = self.sql(expression, "this")
1280        columns = self.expressions(expression, key="columns", flat=True)
1281        columns = f"({columns})" if columns else ""
1282
1283        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1284            columns = ""
1285            self.unsupported("Named columns are not supported in table alias.")
1286
1287        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1288            alias = self._next_name()
1289
1290        return f"{alias}{columns}"
1291
1292    def bitstring_sql(self, expression: exp.BitString) -> str:
1293        this = self.sql(expression, "this")
1294        if self.dialect.BIT_START:
1295            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1296        return f"{int(this, 2)}"
1297
1298    def hexstring_sql(
1299        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
1300    ) -> str:
1301        this = self.sql(expression, "this")
1302        is_integer_type = expression.args.get("is_integer")
1303
1304        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
1305            not self.dialect.HEX_START and not binary_function_repr
1306        ):
1307            # Integer representation will be returned if:
1308            # - The read dialect treats the hex value as integer literal but not the write
1309            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
1310            return f"{int(this, 16)}"
1311
1312        if not is_integer_type:
1313            # Read dialect treats the hex value as BINARY/BLOB
1314            if binary_function_repr:
1315                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
1316                return self.func(binary_function_repr, exp.Literal.string(this))
1317            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
1318                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
1319                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
1320
1321        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1322
1323    def bytestring_sql(self, expression: exp.ByteString) -> str:
1324        this = self.sql(expression, "this")
1325        if self.dialect.BYTE_START:
1326            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1327        return this
1328
1329    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1330        this = self.sql(expression, "this")
1331        escape = expression.args.get("escape")
1332
1333        if self.dialect.UNICODE_START:
1334            escape_substitute = r"\\\1"
1335            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
1336        else:
1337            escape_substitute = r"\\u\1"
1338            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
1339
1340        if escape:
1341            escape_pattern = re.compile(rf"{escape.name}(\d+)")
1342            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
1343        else:
1344            escape_pattern = ESCAPED_UNICODE_RE
1345            escape_sql = ""
1346
1347        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1348            this = escape_pattern.sub(escape_substitute, this)
1349
1350        return f"{left_quote}{this}{right_quote}{escape_sql}"
1351
1352    def rawstring_sql(self, expression: exp.RawString) -> str:
1353        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
1354        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1355
1356    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1357        this = self.sql(expression, "this")
1358        specifier = self.sql(expression, "expression")
1359        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1360        return f"{this}{specifier}"
1361
1362    def datatype_sql(self, expression: exp.DataType) -> str:
1363        nested = ""
1364        values = ""
1365        interior = self.expressions(expression, flat=True)
1366
1367        type_value = expression.this
1368        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1369            type_sql = self.sql(expression, "kind")
1370        else:
1371            type_sql = (
1372                self.TYPE_MAPPING.get(type_value, type_value.value)
1373                if isinstance(type_value, exp.DataType.Type)
1374                else type_value
1375            )
1376
1377        if interior:
1378            if expression.args.get("nested"):
1379                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1380                if expression.args.get("values") is not None:
1381                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1382                    values = self.expressions(expression, key="values", flat=True)
1383                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1384            elif type_value == exp.DataType.Type.INTERVAL:
1385                nested = f" {interior}"
1386            else:
1387                nested = f"({interior})"
1388
1389        type_sql = f"{type_sql}{nested}{values}"
1390        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1391            exp.DataType.Type.TIMETZ,
1392            exp.DataType.Type.TIMESTAMPTZ,
1393        ):
1394            type_sql = f"{type_sql} WITH TIME ZONE"
1395
1396        return type_sql
1397
1398    def directory_sql(self, expression: exp.Directory) -> str:
1399        local = "LOCAL " if expression.args.get("local") else ""
1400        row_format = self.sql(expression, "row_format")
1401        row_format = f" {row_format}" if row_format else ""
1402        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1403
1404    def delete_sql(self, expression: exp.Delete) -> str:
1405        this = self.sql(expression, "this")
1406        this = f" FROM {this}" if this else ""
1407        using = self.sql(expression, "using")
1408        using = f" USING {using}" if using else ""
1409        cluster = self.sql(expression, "cluster")
1410        cluster = f" {cluster}" if cluster else ""
1411        where = self.sql(expression, "where")
1412        returning = self.sql(expression, "returning")
1413        limit = self.sql(expression, "limit")
1414        tables = self.expressions(expression, key="tables")
1415        tables = f" {tables}" if tables else ""
1416        if self.RETURNING_END:
1417            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
1418        else:
1419            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
1420        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1421
1422    def drop_sql(self, expression: exp.Drop) -> str:
1423        this = self.sql(expression, "this")
1424        expressions = self.expressions(expression, flat=True)
1425        expressions = f" ({expressions})" if expressions else ""
1426        kind = expression.args["kind"]
1427        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1428        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1429        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1430        on_cluster = self.sql(expression, "cluster")
1431        on_cluster = f" {on_cluster}" if on_cluster else ""
1432        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1433        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1434        cascade = " CASCADE" if expression.args.get("cascade") else ""
1435        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1436        purge = " PURGE" if expression.args.get("purge") else ""
1437        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
1438
1439    def set_operation(self, expression: exp.SetOperation) -> str:
1440        op_type = type(expression)
1441        op_name = op_type.key.upper()
1442
1443        distinct = expression.args.get("distinct")
1444        if (
1445            distinct is False
1446            and op_type in (exp.Except, exp.Intersect)
1447            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
1448        ):
1449            self.unsupported(f"{op_name} ALL is not supported")
1450
1451        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
1452
1453        if distinct is None:
1454            distinct = default_distinct
1455            if distinct is None:
1456                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
1457
1458        if distinct is default_distinct:
1459            distinct_or_all = ""
1460        else:
1461            distinct_or_all = " DISTINCT" if distinct else " ALL"
1462
1463        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
1464        side_kind = f"{side_kind} " if side_kind else ""
1465
1466        by_name = " BY NAME" if expression.args.get("by_name") else ""
1467        on = self.expressions(expression, key="on", flat=True)
1468        on = f" ON ({on})" if on else ""
1469
1470        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
1471
1472    def set_operations(self, expression: exp.SetOperation) -> str:
1473        if not self.SET_OP_MODIFIERS:
1474            limit = expression.args.get("limit")
1475            order = expression.args.get("order")
1476
1477            if limit or order:
1478                select = self._move_ctes_to_top_level(
1479                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1480                )
1481
1482                if limit:
1483                    select = select.limit(limit.pop(), copy=False)
1484                if order:
1485                    select = select.order_by(order.pop(), copy=False)
1486                return self.sql(select)
1487
1488        sqls: t.List[str] = []
1489        stack: t.List[t.Union[str, exp.Expression]] = [expression]
1490
1491        while stack:
1492            node = stack.pop()
1493
1494            if isinstance(node, exp.SetOperation):
1495                stack.append(node.expression)
1496                stack.append(
1497                    self.maybe_comment(
1498                        self.set_operation(node), comments=node.comments, separated=True
1499                    )
1500                )
1501                stack.append(node.this)
1502            else:
1503                sqls.append(self.sql(node))
1504
1505        this = self.sep().join(sqls)
1506        this = self.query_modifiers(expression, this)
1507        return self.prepend_ctes(expression, this)
1508
1509    def fetch_sql(self, expression: exp.Fetch) -> str:
1510        direction = expression.args.get("direction")
1511        direction = f" {direction}" if direction else ""
1512        count = self.sql(expression, "count")
1513        count = f" {count}" if count else ""
1514        limit_options = self.sql(expression, "limit_options")
1515        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
1516        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
1517
1518    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
1519        percent = " PERCENT" if expression.args.get("percent") else ""
1520        rows = " ROWS" if expression.args.get("rows") else ""
1521        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
1522        if not with_ties and rows:
1523            with_ties = " ONLY"
1524        return f"{percent}{rows}{with_ties}"
1525
1526    def filter_sql(self, expression: exp.Filter) -> str:
1527        if self.AGGREGATE_FILTER_SUPPORTED:
1528            this = self.sql(expression, "this")
1529            where = self.sql(expression, "expression").strip()
1530            return f"{this} FILTER({where})"
1531
1532        agg = expression.this
1533        agg_arg = agg.this
1534        cond = expression.expression.this
1535        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1536        return self.sql(agg)
1537
1538    def hint_sql(self, expression: exp.Hint) -> str:
1539        if not self.QUERY_HINTS:
1540            self.unsupported("Hints are not supported")
1541            return ""
1542
1543        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1544
1545    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
1546        using = self.sql(expression, "using")
1547        using = f" USING {using}" if using else ""
1548        columns = self.expressions(expression, key="columns", flat=True)
1549        columns = f"({columns})" if columns else ""
1550        partition_by = self.expressions(expression, key="partition_by", flat=True)
1551        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1552        where = self.sql(expression, "where")
1553        include = self.expressions(expression, key="include", flat=True)
1554        if include:
1555            include = f" INCLUDE ({include})"
1556        with_storage = self.expressions(expression, key="with_storage", flat=True)
1557        with_storage = f" WITH ({with_storage})" if with_storage else ""
1558        tablespace = self.sql(expression, "tablespace")
1559        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
1560        on = self.sql(expression, "on")
1561        on = f" ON {on}" if on else ""
1562
1563        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
1564
1565    def index_sql(self, expression: exp.Index) -> str:
1566        unique = "UNIQUE " if expression.args.get("unique") else ""
1567        primary = "PRIMARY " if expression.args.get("primary") else ""
1568        amp = "AMP " if expression.args.get("amp") else ""
1569        name = self.sql(expression, "this")
1570        name = f"{name} " if name else ""
1571        table = self.sql(expression, "table")
1572        table = f"{self.INDEX_ON} {table}" if table else ""
1573
1574        index = "INDEX " if not table else ""
1575
1576        params = self.sql(expression, "params")
1577        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1578
1579    def identifier_sql(self, expression: exp.Identifier) -> str:
1580        text = expression.name
1581        lower = text.lower()
1582        text = lower if self.normalize and not expression.quoted else text
1583        text = text.replace(self._identifier_end, self._escaped_identifier_end)
1584        if (
1585            expression.quoted
1586            or self.dialect.can_identify(text, self.identify)
1587            or lower in self.RESERVED_KEYWORDS
1588            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1589        ):
1590            text = f"{self._identifier_start}{text}{self._identifier_end}"
1591        return text
1592
1593    def hex_sql(self, expression: exp.Hex) -> str:
1594        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1595        if self.dialect.HEX_LOWERCASE:
1596            text = self.func("LOWER", text)
1597
1598        return text
1599
1600    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
1601        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1602        if not self.dialect.HEX_LOWERCASE:
1603            text = self.func("LOWER", text)
1604        return text
1605
1606    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1607        input_format = self.sql(expression, "input_format")
1608        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1609        output_format = self.sql(expression, "output_format")
1610        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1611        return self.sep().join((input_format, output_format))
1612
1613    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1614        string = self.sql(exp.Literal.string(expression.name))
1615        return f"{prefix}{string}"
1616
1617    def partition_sql(self, expression: exp.Partition) -> str:
1618        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
1619        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
1620
1621    def properties_sql(self, expression: exp.Properties) -> str:
1622        root_properties = []
1623        with_properties = []
1624
1625        for p in expression.expressions:
1626            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1627            if p_loc == exp.Properties.Location.POST_WITH:
1628                with_properties.append(p)
1629            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1630                root_properties.append(p)
1631
1632        root_props = self.root_properties(exp.Properties(expressions=root_properties))
1633        with_props = self.with_properties(exp.Properties(expressions=with_properties))
1634
1635        if root_props and with_props and not self.pretty:
1636            with_props = " " + with_props
1637
1638        return root_props + with_props
1639
1640    def root_properties(self, properties: exp.Properties) -> str:
1641        if properties.expressions:
1642            return self.expressions(properties, indent=False, sep=" ")
1643        return ""
1644
1645    def properties(
1646        self,
1647        properties: exp.Properties,
1648        prefix: str = "",
1649        sep: str = ", ",
1650        suffix: str = "",
1651        wrapped: bool = True,
1652    ) -> str:
1653        if properties.expressions:
1654            expressions = self.expressions(properties, sep=sep, indent=False)
1655            if expressions:
1656                expressions = self.wrap(expressions) if wrapped else expressions
1657                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1658        return ""
1659
1660    def with_properties(self, properties: exp.Properties) -> str:
1661        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
1662
1663    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1664        properties_locs = defaultdict(list)
1665        for p in properties.expressions:
1666            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1667            if p_loc != exp.Properties.Location.UNSUPPORTED:
1668                properties_locs[p_loc].append(p)
1669            else:
1670                self.unsupported(f"Unsupported property {p.key}")
1671
1672        return properties_locs
1673
1674    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1675        if isinstance(expression.this, exp.Dot):
1676            return self.sql(expression, "this")
1677        return f"'{expression.name}'" if string_key else expression.name
1678
1679    def property_sql(self, expression: exp.Property) -> str:
1680        property_cls = expression.__class__
1681        if property_cls == exp.Property:
1682            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1683
1684        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1685        if not property_name:
1686            self.unsupported(f"Unsupported property {expression.key}")
1687
1688        return f"{property_name}={self.sql(expression, 'this')}"
1689
1690    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1691        if self.SUPPORTS_CREATE_TABLE_LIKE:
1692            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1693            options = f" {options}" if options else ""
1694
1695            like = f"LIKE {self.sql(expression, 'this')}{options}"
1696            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1697                like = f"({like})"
1698
1699            return like
1700
1701        if expression.expressions:
1702            self.unsupported("Transpilation of LIKE property options is unsupported")
1703
1704        select = exp.select("*").from_(expression.this).limit(0)
1705        return f"AS {self.sql(select)}"
1706
1707    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1708        no = "NO " if expression.args.get("no") else ""
1709        protection = " PROTECTION" if expression.args.get("protection") else ""
1710        return f"{no}FALLBACK{protection}"
1711
1712    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1713        no = "NO " if expression.args.get("no") else ""
1714        local = expression.args.get("local")
1715        local = f"{local} " if local else ""
1716        dual = "DUAL " if expression.args.get("dual") else ""
1717        before = "BEFORE " if expression.args.get("before") else ""
1718        after = "AFTER " if expression.args.get("after") else ""
1719        return f"{no}{local}{dual}{before}{after}JOURNAL"
1720
1721    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1722        freespace = self.sql(expression, "this")
1723        percent = " PERCENT" if expression.args.get("percent") else ""
1724        return f"FREESPACE={freespace}{percent}"
1725
1726    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1727        if expression.args.get("default"):
1728            property = "DEFAULT"
1729        elif expression.args.get("on"):
1730            property = "ON"
1731        else:
1732            property = "OFF"
1733        return f"CHECKSUM={property}"
1734
1735    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1736        if expression.args.get("no"):
1737            return "NO MERGEBLOCKRATIO"
1738        if expression.args.get("default"):
1739            return "DEFAULT MERGEBLOCKRATIO"
1740
1741        percent = " PERCENT" if expression.args.get("percent") else ""
1742        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1743
1744    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1745        default = expression.args.get("default")
1746        minimum = expression.args.get("minimum")
1747        maximum = expression.args.get("maximum")
1748        if default or minimum or maximum:
1749            if default:
1750                prop = "DEFAULT"
1751            elif minimum:
1752                prop = "MINIMUM"
1753            else:
1754                prop = "MAXIMUM"
1755            return f"{prop} DATABLOCKSIZE"
1756        units = expression.args.get("units")
1757        units = f" {units}" if units else ""
1758        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1759
1760    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1761        autotemp = expression.args.get("autotemp")
1762        always = expression.args.get("always")
1763        default = expression.args.get("default")
1764        manual = expression.args.get("manual")
1765        never = expression.args.get("never")
1766
1767        if autotemp is not None:
1768            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1769        elif always:
1770            prop = "ALWAYS"
1771        elif default:
1772            prop = "DEFAULT"
1773        elif manual:
1774            prop = "MANUAL"
1775        elif never:
1776            prop = "NEVER"
1777        return f"BLOCKCOMPRESSION={prop}"
1778
1779    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1780        no = expression.args.get("no")
1781        no = " NO" if no else ""
1782        concurrent = expression.args.get("concurrent")
1783        concurrent = " CONCURRENT" if concurrent else ""
1784        target = self.sql(expression, "target")
1785        target = f" {target}" if target else ""
1786        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1787
1788    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1789        if isinstance(expression.this, list):
1790            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1791        if expression.this:
1792            modulus = self.sql(expression, "this")
1793            remainder = self.sql(expression, "expression")
1794            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1795
1796        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1797        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1798        return f"FROM ({from_expressions}) TO ({to_expressions})"
1799
1800    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1801        this = self.sql(expression, "this")
1802
1803        for_values_or_default = expression.expression
1804        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1805            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1806        else:
1807            for_values_or_default = " DEFAULT"
1808
1809        return f"PARTITION OF {this}{for_values_or_default}"
1810
1811    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1812        kind = expression.args.get("kind")
1813        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1814        for_or_in = expression.args.get("for_or_in")
1815        for_or_in = f" {for_or_in}" if for_or_in else ""
1816        lock_type = expression.args.get("lock_type")
1817        override = " OVERRIDE" if expression.args.get("override") else ""
1818        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1819
1820    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1821        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1822        statistics = expression.args.get("statistics")
1823        statistics_sql = ""
1824        if statistics is not None:
1825            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1826        return f"{data_sql}{statistics_sql}"
1827
1828    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1829        this = self.sql(expression, "this")
1830        this = f"HISTORY_TABLE={this}" if this else ""
1831        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
1832        data_consistency = (
1833            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
1834        )
1835        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
1836        retention_period = (
1837            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
1838        )
1839
1840        if this:
1841            on_sql = self.func("ON", this, data_consistency, retention_period)
1842        else:
1843            on_sql = "ON" if expression.args.get("on") else "OFF"
1844
1845        sql = f"SYSTEM_VERSIONING={on_sql}"
1846
1847        return f"WITH({sql})" if expression.args.get("with") else sql
1848
1849    def insert_sql(self, expression: exp.Insert) -> str:
1850        hint = self.sql(expression, "hint")
1851        overwrite = expression.args.get("overwrite")
1852
1853        if isinstance(expression.this, exp.Directory):
1854            this = " OVERWRITE" if overwrite else " INTO"
1855        else:
1856            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1857
1858        stored = self.sql(expression, "stored")
1859        stored = f" {stored}" if stored else ""
1860        alternative = expression.args.get("alternative")
1861        alternative = f" OR {alternative}" if alternative else ""
1862        ignore = " IGNORE" if expression.args.get("ignore") else ""
1863        is_function = expression.args.get("is_function")
1864        if is_function:
1865            this = f"{this} FUNCTION"
1866        this = f"{this} {self.sql(expression, 'this')}"
1867
1868        exists = " IF EXISTS" if expression.args.get("exists") else ""
1869        where = self.sql(expression, "where")
1870        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1871        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1872        on_conflict = self.sql(expression, "conflict")
1873        on_conflict = f" {on_conflict}" if on_conflict else ""
1874        by_name = " BY NAME" if expression.args.get("by_name") else ""
1875        returning = self.sql(expression, "returning")
1876
1877        if self.RETURNING_END:
1878            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1879        else:
1880            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1881
1882        partition_by = self.sql(expression, "partition")
1883        partition_by = f" {partition_by}" if partition_by else ""
1884        settings = self.sql(expression, "settings")
1885        settings = f" {settings}" if settings else ""
1886
1887        source = self.sql(expression, "source")
1888        source = f"TABLE {source}" if source else ""
1889
1890        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
1891        return self.prepend_ctes(expression, sql)
1892
1893    def introducer_sql(self, expression: exp.Introducer) -> str:
1894        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1895
1896    def kill_sql(self, expression: exp.Kill) -> str:
1897        kind = self.sql(expression, "kind")
1898        kind = f" {kind}" if kind else ""
1899        this = self.sql(expression, "this")
1900        this = f" {this}" if this else ""
1901        return f"KILL{kind}{this}"
1902
1903    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1904        return expression.name
1905
1906    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1907        return expression.name
1908
1909    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1910        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1911
1912        constraint = self.sql(expression, "constraint")
1913        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1914
1915        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1916        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1917        action = self.sql(expression, "action")
1918
1919        expressions = self.expressions(expression, flat=True)
1920        if expressions:
1921            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1922            expressions = f" {set_keyword}{expressions}"
1923
1924        where = self.sql(expression, "where")
1925        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
1926
1927    def returning_sql(self, expression: exp.Returning) -> str:
1928        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1929
1930    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1931        fields = self.sql(expression, "fields")
1932        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1933        escaped = self.sql(expression, "escaped")
1934        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1935        items = self.sql(expression, "collection_items")
1936        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1937        keys = self.sql(expression, "map_keys")
1938        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1939        lines = self.sql(expression, "lines")
1940        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1941        null = self.sql(expression, "null")
1942        null = f" NULL DEFINED AS {null}" if null else ""
1943        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1944
1945    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1946        return f"WITH ({self.expressions(expression, flat=True)})"
1947
1948    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1949        this = f"{self.sql(expression, 'this')} INDEX"
1950        target = self.sql(expression, "target")
1951        target = f" FOR {target}" if target else ""
1952        return f"{this}{target} ({self.expressions(expression, flat=True)})"
1953
1954    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1955        this = self.sql(expression, "this")
1956        kind = self.sql(expression, "kind")
1957        expr = self.sql(expression, "expression")
1958        return f"{this} ({kind} => {expr})"
1959
1960    def table_parts(self, expression: exp.Table) -> str:
1961        return ".".join(
1962            self.sql(part)
1963            for part in (
1964                expression.args.get("catalog"),
1965                expression.args.get("db"),
1966                expression.args.get("this"),
1967            )
1968            if part is not None
1969        )
1970
1971    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1972        table = self.table_parts(expression)
1973        only = "ONLY " if expression.args.get("only") else ""
1974        partition = self.sql(expression, "partition")
1975        partition = f" {partition}" if partition else ""
1976        version = self.sql(expression, "version")
1977        version = f" {version}" if version else ""
1978        alias = self.sql(expression, "alias")
1979        alias = f"{sep}{alias}" if alias else ""
1980
1981        sample = self.sql(expression, "sample")
1982        if self.dialect.ALIAS_POST_TABLESAMPLE:
1983            sample_pre_alias = sample
1984            sample_post_alias = ""
1985        else:
1986            sample_pre_alias = ""
1987            sample_post_alias = sample
1988
1989        hints = self.expressions(expression, key="hints", sep=" ")
1990        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1991        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
1992        joins = self.indent(
1993            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
1994        )
1995        laterals = self.expressions(expression, key="laterals", sep="")
1996
1997        file_format = self.sql(expression, "format")
1998        if file_format:
1999            pattern = self.sql(expression, "pattern")
2000            pattern = f", PATTERN => {pattern}" if pattern else ""
2001            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2002
2003        ordinality = expression.args.get("ordinality") or ""
2004        if ordinality:
2005            ordinality = f" WITH ORDINALITY{alias}"
2006            alias = ""
2007
2008        when = self.sql(expression, "when")
2009        if when:
2010            table = f"{table} {when}"
2011
2012        changes = self.sql(expression, "changes")
2013        changes = f" {changes}" if changes else ""
2014
2015        rows_from = self.expressions(expression, key="rows_from")
2016        if rows_from:
2017            table = f"ROWS FROM {self.wrap(rows_from)}"
2018
2019        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
2020
2021    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
2022        table = self.func("TABLE", expression.this)
2023        alias = self.sql(expression, "alias")
2024        alias = f" AS {alias}" if alias else ""
2025        sample = self.sql(expression, "sample")
2026        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2027        joins = self.indent(
2028            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2029        )
2030        return f"{table}{alias}{pivots}{sample}{joins}"
2031
2032    def tablesample_sql(
2033        self,
2034        expression: exp.TableSample,
2035        tablesample_keyword: t.Optional[str] = None,
2036    ) -> str:
2037        method = self.sql(expression, "method")
2038        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
2039        numerator = self.sql(expression, "bucket_numerator")
2040        denominator = self.sql(expression, "bucket_denominator")
2041        field = self.sql(expression, "bucket_field")
2042        field = f" ON {field}" if field else ""
2043        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
2044        seed = self.sql(expression, "seed")
2045        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
2046
2047        size = self.sql(expression, "size")
2048        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
2049            size = f"{size} ROWS"
2050
2051        percent = self.sql(expression, "percent")
2052        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
2053            percent = f"{percent} PERCENT"
2054
2055        expr = f"{bucket}{percent}{size}"
2056        if self.TABLESAMPLE_REQUIRES_PARENS:
2057            expr = f"({expr})"
2058
2059        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
2060
2061    def pivot_sql(self, expression: exp.Pivot) -> str:
2062        expressions = self.expressions(expression, flat=True)
2063        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
2064
2065        group = self.sql(expression, "group")
2066
2067        if expression.this:
2068            this = self.sql(expression, "this")
2069            if not expressions:
2070                return f"UNPIVOT {this}"
2071
2072            on = f"{self.seg('ON')} {expressions}"
2073            into = self.sql(expression, "into")
2074            into = f"{self.seg('INTO')} {into}" if into else ""
2075            using = self.expressions(expression, key="using", flat=True)
2076            using = f"{self.seg('USING')} {using}" if using else ""
2077            return f"{direction} {this}{on}{into}{using}{group}"
2078
2079        alias = self.sql(expression, "alias")
2080        alias = f" AS {alias}" if alias else ""
2081
2082        fields = self.expressions(
2083            expression,
2084            "fields",
2085            sep=" ",
2086            dynamic=True,
2087            new_line=True,
2088            skip_first=True,
2089            skip_last=True,
2090        )
2091
2092        include_nulls = expression.args.get("include_nulls")
2093        if include_nulls is not None:
2094            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
2095        else:
2096            nulls = ""
2097
2098        default_on_null = self.sql(expression, "default_on_null")
2099        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
2100        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
2101
2102    def version_sql(self, expression: exp.Version) -> str:
2103        this = f"FOR {expression.name}"
2104        kind = expression.text("kind")
2105        expr = self.sql(expression, "expression")
2106        return f"{this} {kind} {expr}"
2107
2108    def tuple_sql(self, expression: exp.Tuple) -> str:
2109        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
2110
2111    def update_sql(self, expression: exp.Update) -> str:
2112        this = self.sql(expression, "this")
2113        set_sql = self.expressions(expression, flat=True)
2114        from_sql = self.sql(expression, "from")
2115        where_sql = self.sql(expression, "where")
2116        returning = self.sql(expression, "returning")
2117        order = self.sql(expression, "order")
2118        limit = self.sql(expression, "limit")
2119        if self.RETURNING_END:
2120            expression_sql = f"{from_sql}{where_sql}{returning}"
2121        else:
2122            expression_sql = f"{returning}{from_sql}{where_sql}"
2123        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
2124        return self.prepend_ctes(expression, sql)
2125
2126    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
2127        values_as_table = values_as_table and self.VALUES_AS_TABLE
2128
2129        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
2130        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
2131            args = self.expressions(expression)
2132            alias = self.sql(expression, "alias")
2133            values = f"VALUES{self.seg('')}{args}"
2134            values = (
2135                f"({values})"
2136                if self.WRAP_DERIVED_VALUES
2137                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
2138                else values
2139            )
2140            return f"{values} AS {alias}" if alias else values
2141
2142        # Converts `VALUES...` expression into a series of select unions.
2143        alias_node = expression.args.get("alias")
2144        column_names = alias_node and alias_node.columns
2145
2146        selects: t.List[exp.Query] = []
2147
2148        for i, tup in enumerate(expression.expressions):
2149            row = tup.expressions
2150
2151            if i == 0 and column_names:
2152                row = [
2153                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
2154                ]
2155
2156            selects.append(exp.Select(expressions=row))
2157
2158        if self.pretty:
2159            # This may result in poor performance for large-cardinality `VALUES` tables, due to
2160            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
2161            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
2162            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
2163            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
2164
2165        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
2166        unions = " UNION ALL ".join(self.sql(select) for select in selects)
2167        return f"({unions}){alias}"
2168
2169    def var_sql(self, expression: exp.Var) -> str:
2170        return self.sql(expression, "this")
2171
2172    @unsupported_args("expressions")
2173    def into_sql(self, expression: exp.Into) -> str:
2174        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
2175        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
2176        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
2177
2178    def from_sql(self, expression: exp.From) -> str:
2179        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
2180
2181    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
2182        grouping_sets = self.expressions(expression, indent=False)
2183        return f"GROUPING SETS {self.wrap(grouping_sets)}"
2184
2185    def rollup_sql(self, expression: exp.Rollup) -> str:
2186        expressions = self.expressions(expression, indent=False)
2187        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
2188
2189    def cube_sql(self, expression: exp.Cube) -> str:
2190        expressions = self.expressions(expression, indent=False)
2191        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
2192
2193    def group_sql(self, expression: exp.Group) -> str:
2194        group_by_all = expression.args.get("all")
2195        if group_by_all is True:
2196            modifier = " ALL"
2197        elif group_by_all is False:
2198            modifier = " DISTINCT"
2199        else:
2200            modifier = ""
2201
2202        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
2203
2204        grouping_sets = self.expressions(expression, key="grouping_sets")
2205        cube = self.expressions(expression, key="cube")
2206        rollup = self.expressions(expression, key="rollup")
2207
2208        groupings = csv(
2209            self.seg(grouping_sets) if grouping_sets else "",
2210            self.seg(cube) if cube else "",
2211            self.seg(rollup) if rollup else "",
2212            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
2213            sep=self.GROUPINGS_SEP,
2214        )
2215
2216        if (
2217            expression.expressions
2218            and groupings
2219            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2220        ):
2221            group_by = f"{group_by}{self.GROUPINGS_SEP}"
2222
2223        return f"{group_by}{groupings}"
2224
2225    def having_sql(self, expression: exp.Having) -> str:
2226        this = self.indent(self.sql(expression, "this"))
2227        return f"{self.seg('HAVING')}{self.sep()}{this}"
2228
2229    def connect_sql(self, expression: exp.Connect) -> str:
2230        start = self.sql(expression, "start")
2231        start = self.seg(f"START WITH {start}") if start else ""
2232        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
2233        connect = self.sql(expression, "connect")
2234        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
2235        return start + connect
2236
2237    def prior_sql(self, expression: exp.Prior) -> str:
2238        return f"PRIOR {self.sql(expression, 'this')}"
2239
2240    def join_sql(self, expression: exp.Join) -> str:
2241        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2242            side = None
2243        else:
2244            side = expression.side
2245
2246        op_sql = " ".join(
2247            op
2248            for op in (
2249                expression.method,
2250                "GLOBAL" if expression.args.get("global") else None,
2251                side,
2252                expression.kind,
2253                expression.hint if self.JOIN_HINTS else None,
2254            )
2255            if op
2256        )
2257        match_cond = self.sql(expression, "match_condition")
2258        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2259        on_sql = self.sql(expression, "on")
2260        using = expression.args.get("using")
2261
2262        if not on_sql and using:
2263            on_sql = csv(*(self.sql(column) for column in using))
2264
2265        this = expression.this
2266        this_sql = self.sql(this)
2267
2268        exprs = self.expressions(expression)
2269        if exprs:
2270            this_sql = f"{this_sql},{self.seg(exprs)}"
2271
2272        if on_sql:
2273            on_sql = self.indent(on_sql, skip_first=True)
2274            space = self.seg(" " * self.pad) if self.pretty else " "
2275            if using:
2276                on_sql = f"{space}USING ({on_sql})"
2277            else:
2278                on_sql = f"{space}ON {on_sql}"
2279        elif not op_sql:
2280            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2281                return f" {this_sql}"
2282
2283            return f", {this_sql}"
2284
2285        if op_sql != "STRAIGHT_JOIN":
2286            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2287
2288        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
2289
2290    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
2291        args = self.expressions(expression, flat=True)
2292        args = f"({args})" if len(args.split(",")) > 1 else args
2293        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
2294
2295    def lateral_op(self, expression: exp.Lateral) -> str:
2296        cross_apply = expression.args.get("cross_apply")
2297
2298        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
2299        if cross_apply is True:
2300            op = "INNER JOIN "
2301        elif cross_apply is False:
2302            op = "LEFT JOIN "
2303        else:
2304            op = ""
2305
2306        return f"{op}LATERAL"
2307
2308    def lateral_sql(self, expression: exp.Lateral) -> str:
2309        this = self.sql(expression, "this")
2310
2311        if expression.args.get("view"):
2312            alias = expression.args["alias"]
2313            columns = self.expressions(alias, key="columns", flat=True)
2314            table = f" {alias.name}" if alias.name else ""
2315            columns = f" AS {columns}" if columns else ""
2316            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
2317            return f"{op_sql}{self.sep()}{this}{table}{columns}"
2318
2319        alias = self.sql(expression, "alias")
2320        alias = f" AS {alias}" if alias else ""
2321
2322        ordinality = expression.args.get("ordinality") or ""
2323        if ordinality:
2324            ordinality = f" WITH ORDINALITY{alias}"
2325            alias = ""
2326
2327        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
2328
2329    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2330        this = self.sql(expression, "this")
2331
2332        args = [
2333            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2334            for e in (expression.args.get(k) for k in ("offset", "expression"))
2335            if e
2336        ]
2337
2338        args_sql = ", ".join(self.sql(e) for e in args)
2339        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
2340        expressions = self.expressions(expression, flat=True)
2341        limit_options = self.sql(expression, "limit_options")
2342        expressions = f" BY {expressions}" if expressions else ""
2343
2344        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
2345
2346    def offset_sql(self, expression: exp.Offset) -> str:
2347        this = self.sql(expression, "this")
2348        value = expression.expression
2349        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
2350        expressions = self.expressions(expression, flat=True)
2351        expressions = f" BY {expressions}" if expressions else ""
2352        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
2353
2354    def setitem_sql(self, expression: exp.SetItem) -> str:
2355        kind = self.sql(expression, "kind")
2356        kind = f"{kind} " if kind else ""
2357        this = self.sql(expression, "this")
2358        expressions = self.expressions(expression)
2359        collate = self.sql(expression, "collate")
2360        collate = f" COLLATE {collate}" if collate else ""
2361        global_ = "GLOBAL " if expression.args.get("global") else ""
2362        return f"{global_}{kind}{this}{expressions}{collate}"
2363
2364    def set_sql(self, expression: exp.Set) -> str:
2365        expressions = f" {self.expressions(expression, flat=True)}"
2366        tag = " TAG" if expression.args.get("tag") else ""
2367        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2368
2369    def pragma_sql(self, expression: exp.Pragma) -> str:
2370        return f"PRAGMA {self.sql(expression, 'this')}"
2371
2372    def lock_sql(self, expression: exp.Lock) -> str:
2373        if not self.LOCKING_READS_SUPPORTED:
2374            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
2375            return ""
2376
2377        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
2378        expressions = self.expressions(expression, flat=True)
2379        expressions = f" OF {expressions}" if expressions else ""
2380        wait = expression.args.get("wait")
2381
2382        if wait is not None:
2383            if isinstance(wait, exp.Literal):
2384                wait = f" WAIT {self.sql(wait)}"
2385            else:
2386                wait = " NOWAIT" if wait else " SKIP LOCKED"
2387
2388        return f"{lock_type}{expressions}{wait or ''}"
2389
2390    def literal_sql(self, expression: exp.Literal) -> str:
2391        text = expression.this or ""
2392        if expression.is_string:
2393            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
2394        return text
2395
2396    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2397        if self.dialect.ESCAPED_SEQUENCES:
2398            to_escaped = self.dialect.ESCAPED_SEQUENCES
2399            text = "".join(
2400                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
2401            )
2402
2403        return self._replace_line_breaks(text).replace(
2404            self.dialect.QUOTE_END, self._escaped_quote_end
2405        )
2406
2407    def loaddata_sql(self, expression: exp.LoadData) -> str:
2408        local = " LOCAL" if expression.args.get("local") else ""
2409        inpath = f" INPATH {self.sql(expression, 'inpath')}"
2410        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
2411        this = f" INTO TABLE {self.sql(expression, 'this')}"
2412        partition = self.sql(expression, "partition")
2413        partition = f" {partition}" if partition else ""
2414        input_format = self.sql(expression, "input_format")
2415        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
2416        serde = self.sql(expression, "serde")
2417        serde = f" SERDE {serde}" if serde else ""
2418        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2419
2420    def null_sql(self, *_) -> str:
2421        return "NULL"
2422
2423    def boolean_sql(self, expression: exp.Boolean) -> str:
2424        return "TRUE" if expression.this else "FALSE"
2425
2426    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
2427        this = self.sql(expression, "this")
2428        this = f"{this} " if this else this
2429        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2430        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2431
2432    def withfill_sql(self, expression: exp.WithFill) -> str:
2433        from_sql = self.sql(expression, "from")
2434        from_sql = f" FROM {from_sql}" if from_sql else ""
2435        to_sql = self.sql(expression, "to")
2436        to_sql = f" TO {to_sql}" if to_sql else ""
2437        step_sql = self.sql(expression, "step")
2438        step_sql = f" STEP {step_sql}" if step_sql else ""
2439        interpolated_values = [
2440            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
2441            if isinstance(e, exp.Alias)
2442            else self.sql(e, "this")
2443            for e in expression.args.get("interpolate") or []
2444        ]
2445        interpolate = (
2446            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2447        )
2448        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
2449
2450    def cluster_sql(self, expression: exp.Cluster) -> str:
2451        return self.op_expressions("CLUSTER BY", expression)
2452
2453    def distribute_sql(self, expression: exp.Distribute) -> str:
2454        return self.op_expressions("DISTRIBUTE BY", expression)
2455
2456    def sort_sql(self, expression: exp.Sort) -> str:
2457        return self.op_expressions("SORT BY", expression)
2458
2459    def ordered_sql(self, expression: exp.Ordered) -> str:
2460        desc = expression.args.get("desc")
2461        asc = not desc
2462
2463        nulls_first = expression.args.get("nulls_first")
2464        nulls_last = not nulls_first
2465        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2466        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2467        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2468
2469        this = self.sql(expression, "this")
2470
2471        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2472        nulls_sort_change = ""
2473        if nulls_first and (
2474            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2475        ):
2476            nulls_sort_change = " NULLS FIRST"
2477        elif (
2478            nulls_last
2479            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2480            and not nulls_are_last
2481        ):
2482            nulls_sort_change = " NULLS LAST"
2483
2484        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2485        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2486            window = expression.find_ancestor(exp.Window, exp.Select)
2487            if isinstance(window, exp.Window) and window.args.get("spec"):
2488                self.unsupported(
2489                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2490                )
2491                nulls_sort_change = ""
2492            elif self.NULL_ORDERING_SUPPORTED is False and (
2493                (asc and nulls_sort_change == " NULLS LAST")
2494                or (desc and nulls_sort_change == " NULLS FIRST")
2495            ):
2496                # BigQuery does not allow these ordering/nulls combinations when used under
2497                # an aggregation func or under a window containing one
2498                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
2499
2500                if isinstance(ancestor, exp.Window):
2501                    ancestor = ancestor.this
2502                if isinstance(ancestor, exp.AggFunc):
2503                    self.unsupported(
2504                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
2505                    )
2506                    nulls_sort_change = ""
2507            elif self.NULL_ORDERING_SUPPORTED is None:
2508                if expression.this.is_int:
2509                    self.unsupported(
2510                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2511                    )
2512                elif not isinstance(expression.this, exp.Rand):
2513                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2514                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2515                nulls_sort_change = ""
2516
2517        with_fill = self.sql(expression, "with_fill")
2518        with_fill = f" {with_fill}" if with_fill else ""
2519
2520        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2521
2522    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
2523        window_frame = self.sql(expression, "window_frame")
2524        window_frame = f"{window_frame} " if window_frame else ""
2525
2526        this = self.sql(expression, "this")
2527
2528        return f"{window_frame}{this}"
2529
2530    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2531        partition = self.partition_by_sql(expression)
2532        order = self.sql(expression, "order")
2533        measures = self.expressions(expression, key="measures")
2534        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2535        rows = self.sql(expression, "rows")
2536        rows = self.seg(rows) if rows else ""
2537        after = self.sql(expression, "after")
2538        after = self.seg(after) if after else ""
2539        pattern = self.sql(expression, "pattern")
2540        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2541        definition_sqls = [
2542            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2543            for definition in expression.args.get("define", [])
2544        ]
2545        definitions = self.expressions(sqls=definition_sqls)
2546        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2547        body = "".join(
2548            (
2549                partition,
2550                order,
2551                measures,
2552                rows,
2553                after,
2554                pattern,
2555                define,
2556            )
2557        )
2558        alias = self.sql(expression, "alias")
2559        alias = f" {alias}" if alias else ""
2560        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2561
2562    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2563        limit = expression.args.get("limit")
2564
2565        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2566            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2567        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2568            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2569
2570        return csv(
2571            *sqls,
2572            *[self.sql(join) for join in expression.args.get("joins") or []],
2573            self.sql(expression, "match"),
2574            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2575            self.sql(expression, "prewhere"),
2576            self.sql(expression, "where"),
2577            self.sql(expression, "connect"),
2578            self.sql(expression, "group"),
2579            self.sql(expression, "having"),
2580            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2581            self.sql(expression, "order"),
2582            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
2583            *self.after_limit_modifiers(expression),
2584            self.options_modifier(expression),
2585            sep="",
2586        )
2587
2588    def options_modifier(self, expression: exp.Expression) -> str:
2589        options = self.expressions(expression, key="options")
2590        return f" {options}" if options else ""
2591
2592    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2593        self.unsupported("Unsupported query option.")
2594        return ""
2595
2596    def offset_limit_modifiers(
2597        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2598    ) -> t.List[str]:
2599        return [
2600            self.sql(expression, "offset") if fetch else self.sql(limit),
2601            self.sql(limit) if fetch else self.sql(expression, "offset"),
2602        ]
2603
2604    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2605        locks = self.expressions(expression, key="locks", sep=" ")
2606        locks = f" {locks}" if locks else ""
2607        return [locks, self.sql(expression, "sample")]
2608
2609    def select_sql(self, expression: exp.Select) -> str:
2610        into = expression.args.get("into")
2611        if not self.SUPPORTS_SELECT_INTO and into:
2612            into.pop()
2613
2614        hint = self.sql(expression, "hint")
2615        distinct = self.sql(expression, "distinct")
2616        distinct = f" {distinct}" if distinct else ""
2617        kind = self.sql(expression, "kind")
2618
2619        limit = expression.args.get("limit")
2620        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
2621            top = self.limit_sql(limit, top=True)
2622            limit.pop()
2623        else:
2624            top = ""
2625
2626        expressions = self.expressions(expression)
2627
2628        if kind:
2629            if kind in self.SELECT_KINDS:
2630                kind = f" AS {kind}"
2631            else:
2632                if kind == "STRUCT":
2633                    expressions = self.expressions(
2634                        sqls=[
2635                            self.sql(
2636                                exp.Struct(
2637                                    expressions=[
2638                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2639                                        if isinstance(e, exp.Alias)
2640                                        else e
2641                                        for e in expression.expressions
2642                                    ]
2643                                )
2644                            )
2645                        ]
2646                    )
2647                kind = ""
2648
2649        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
2650        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
2651
2652        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2653        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2654        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2655        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2656        sql = self.query_modifiers(
2657            expression,
2658            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
2659            self.sql(expression, "into", comment=False),
2660            self.sql(expression, "from", comment=False),
2661        )
2662
2663        # If both the CTE and SELECT clauses have comments, generate the latter earlier
2664        if expression.args.get("with"):
2665            sql = self.maybe_comment(sql, expression)
2666            expression.pop_comments()
2667
2668        sql = self.prepend_ctes(expression, sql)
2669
2670        if not self.SUPPORTS_SELECT_INTO and into:
2671            if into.args.get("temporary"):
2672                table_kind = " TEMPORARY"
2673            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2674                table_kind = " UNLOGGED"
2675            else:
2676                table_kind = ""
2677            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2678
2679        return sql
2680
2681    def schema_sql(self, expression: exp.Schema) -> str:
2682        this = self.sql(expression, "this")
2683        sql = self.schema_columns_sql(expression)
2684        return f"{this} {sql}" if this and sql else this or sql
2685
2686    def schema_columns_sql(self, expression: exp.Schema) -> str:
2687        if expression.expressions:
2688            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2689        return ""
2690
2691    def star_sql(self, expression: exp.Star) -> str:
2692        except_ = self.expressions(expression, key="except", flat=True)
2693        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
2694        replace = self.expressions(expression, key="replace", flat=True)
2695        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
2696        rename = self.expressions(expression, key="rename", flat=True)
2697        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
2698        return f"*{except_}{replace}{rename}"
2699
2700    def parameter_sql(self, expression: exp.Parameter) -> str:
2701        this = self.sql(expression, "this")
2702        return f"{self.PARAMETER_TOKEN}{this}"
2703
2704    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2705        this = self.sql(expression, "this")
2706        kind = expression.text("kind")
2707        if kind:
2708            kind = f"{kind}."
2709        return f"@@{kind}{this}"
2710
2711    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2712        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
2713
2714    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2715        alias = self.sql(expression, "alias")
2716        alias = f"{sep}{alias}" if alias else ""
2717        sample = self.sql(expression, "sample")
2718        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
2719            alias = f"{sample}{alias}"
2720
2721            # Set to None so it's not generated again by self.query_modifiers()
2722            expression.set("sample", None)
2723
2724        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2725        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2726        return self.prepend_ctes(expression, sql)
2727
2728    def qualify_sql(self, expression: exp.Qualify) -> str:
2729        this = self.indent(self.sql(expression, "this"))
2730        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2731
2732    def unnest_sql(self, expression: exp.Unnest) -> str:
2733        args = self.expressions(expression, flat=True)
2734
2735        alias = expression.args.get("alias")
2736        offset = expression.args.get("offset")
2737
2738        if self.UNNEST_WITH_ORDINALITY:
2739            if alias and isinstance(offset, exp.Expression):
2740                alias.append("columns", offset)
2741
2742        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2743            columns = alias.columns
2744            alias = self.sql(columns[0]) if columns else ""
2745        else:
2746            alias = self.sql(alias)
2747
2748        alias = f" AS {alias}" if alias else alias
2749        if self.UNNEST_WITH_ORDINALITY:
2750            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2751        else:
2752            if isinstance(offset, exp.Expression):
2753                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2754            elif offset:
2755                suffix = f"{alias} WITH OFFSET"
2756            else:
2757                suffix = alias
2758
2759        return f"UNNEST({args}){suffix}"
2760
2761    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2762        return ""
2763
2764    def where_sql(self, expression: exp.Where) -> str:
2765        this = self.indent(self.sql(expression, "this"))
2766        return f"{self.seg('WHERE')}{self.sep()}{this}"
2767
2768    def window_sql(self, expression: exp.Window) -> str:
2769        this = self.sql(expression, "this")
2770        partition = self.partition_by_sql(expression)
2771        order = expression.args.get("order")
2772        order = self.order_sql(order, flat=True) if order else ""
2773        spec = self.sql(expression, "spec")
2774        alias = self.sql(expression, "alias")
2775        over = self.sql(expression, "over") or "OVER"
2776
2777        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2778
2779        first = expression.args.get("first")
2780        if first is None:
2781            first = ""
2782        else:
2783            first = "FIRST" if first else "LAST"
2784
2785        if not partition and not order and not spec and alias:
2786            return f"{this} {alias}"
2787
2788        args = self.format_args(
2789            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
2790        )
2791        return f"{this} ({args})"
2792
2793    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2794        partition = self.expressions(expression, key="partition_by", flat=True)
2795        return f"PARTITION BY {partition}" if partition else ""
2796
2797    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2798        kind = self.sql(expression, "kind")
2799        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2800        end = (
2801            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2802            or "CURRENT ROW"
2803        )
2804        return f"{kind} BETWEEN {start} AND {end}"
2805
2806    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2807        this = self.sql(expression, "this")
2808        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2809        return f"{this} WITHIN GROUP ({expression_sql})"
2810
2811    def between_sql(self, expression: exp.Between) -> str:
2812        this = self.sql(expression, "this")
2813        low = self.sql(expression, "low")
2814        high = self.sql(expression, "high")
2815        return f"{this} BETWEEN {low} AND {high}"
2816
2817    def bracket_offset_expressions(
2818        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
2819    ) -> t.List[exp.Expression]:
2820        return apply_index_offset(
2821            expression.this,
2822            expression.expressions,
2823            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
2824            dialect=self.dialect,
2825        )
2826
2827    def bracket_sql(self, expression: exp.Bracket) -> str:
2828        expressions = self.bracket_offset_expressions(expression)
2829        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2830        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2831
2832    def all_sql(self, expression: exp.All) -> str:
2833        return f"ALL {self.wrap(expression)}"
2834
2835    def any_sql(self, expression: exp.Any) -> str:
2836        this = self.sql(expression, "this")
2837        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
2838            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2839                this = self.wrap(this)
2840            return f"ANY{this}"
2841        return f"ANY {this}"
2842
2843    def exists_sql(self, expression: exp.Exists) -> str:
2844        return f"EXISTS{self.wrap(expression)}"
2845
2846    def case_sql(self, expression: exp.Case) -> str:
2847        this = self.sql(expression, "this")
2848        statements = [f"CASE {this}" if this else "CASE"]
2849
2850        for e in expression.args["ifs"]:
2851            statements.append(f"WHEN {self.sql(e, 'this')}")
2852            statements.append(f"THEN {self.sql(e, 'true')}")
2853
2854        default = self.sql(expression, "default")
2855
2856        if default:
2857            statements.append(f"ELSE {default}")
2858
2859        statements.append("END")
2860
2861        if self.pretty and self.too_wide(statements):
2862            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2863
2864        return " ".join(statements)
2865
2866    def constraint_sql(self, expression: exp.Constraint) -> str:
2867        this = self.sql(expression, "this")
2868        expressions = self.expressions(expression, flat=True)
2869        return f"CONSTRAINT {this} {expressions}"
2870
2871    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2872        order = expression.args.get("order")
2873        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2874        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2875
2876    def extract_sql(self, expression: exp.Extract) -> str:
2877        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2878        expression_sql = self.sql(expression, "expression")
2879        return f"EXTRACT({this} FROM {expression_sql})"
2880
2881    def trim_sql(self, expression: exp.Trim) -> str:
2882        trim_type = self.sql(expression, "position")
2883
2884        if trim_type == "LEADING":
2885            func_name = "LTRIM"
2886        elif trim_type == "TRAILING":
2887            func_name = "RTRIM"
2888        else:
2889            func_name = "TRIM"
2890
2891        return self.func(func_name, expression.this, expression.expression)
2892
2893    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2894        args = expression.expressions
2895        if isinstance(expression, exp.ConcatWs):
2896            args = args[1:]  # Skip the delimiter
2897
2898        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2899            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
2900
2901        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2902            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2903
2904        return args
2905
2906    def concat_sql(self, expression: exp.Concat) -> str:
2907        expressions = self.convert_concat_args(expression)
2908
2909        # Some dialects don't allow a single-argument CONCAT call
2910        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2911            return self.sql(expressions[0])
2912
2913        return self.func("CONCAT", *expressions)
2914
2915    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2916        return self.func(
2917            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2918        )
2919
2920    def check_sql(self, expression: exp.Check) -> str:
2921        this = self.sql(expression, key="this")
2922        return f"CHECK ({this})"
2923
2924    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2925        expressions = self.expressions(expression, flat=True)
2926        expressions = f" ({expressions})" if expressions else ""
2927        reference = self.sql(expression, "reference")
2928        reference = f" {reference}" if reference else ""
2929        delete = self.sql(expression, "delete")
2930        delete = f" ON DELETE {delete}" if delete else ""
2931        update = self.sql(expression, "update")
2932        update = f" ON UPDATE {update}" if update else ""
2933        options = self.expressions(expression, key="options", flat=True, sep=" ")
2934        options = f" {options}" if options else ""
2935        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
2936
2937    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2938        expressions = self.expressions(expression, flat=True)
2939        options = self.expressions(expression, key="options", flat=True, sep=" ")
2940        options = f" {options}" if options else ""
2941        return f"PRIMARY KEY ({expressions}){options}"
2942
2943    def if_sql(self, expression: exp.If) -> str:
2944        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
2945
2946    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2947        modifier = expression.args.get("modifier")
2948        modifier = f" {modifier}" if modifier else ""
2949        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
2950
2951    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2952        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
2953
2954    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2955        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2956
2957        if expression.args.get("escape"):
2958            path = self.escape_str(path)
2959
2960        if self.QUOTE_JSON_PATH:
2961            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2962
2963        return path
2964
2965    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2966        if isinstance(expression, exp.JSONPathPart):
2967            transform = self.TRANSFORMS.get(expression.__class__)
2968            if not callable(transform):
2969                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2970                return ""
2971
2972            return transform(self, expression)
2973
2974        if isinstance(expression, int):
2975            return str(expression)
2976
2977        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2978            escaped = expression.replace("'", "\\'")
2979            escaped = f"\\'{expression}\\'"
2980        else:
2981            escaped = expression.replace('"', '\\"')
2982            escaped = f'"{escaped}"'
2983
2984        return escaped
2985
2986    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2987        return f"{self.sql(expression, 'this')} FORMAT JSON"
2988
2989    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2990        null_handling = expression.args.get("null_handling")
2991        null_handling = f" {null_handling}" if null_handling else ""
2992
2993        unique_keys = expression.args.get("unique_keys")
2994        if unique_keys is not None:
2995            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2996        else:
2997            unique_keys = ""
2998
2999        return_type = self.sql(expression, "return_type")
3000        return_type = f" RETURNING {return_type}" if return_type else ""
3001        encoding = self.sql(expression, "encoding")
3002        encoding = f" ENCODING {encoding}" if encoding else ""
3003
3004        return self.func(
3005            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
3006            *expression.expressions,
3007            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
3008        )
3009
3010    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
3011        return self.jsonobject_sql(expression)
3012
3013    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
3014        null_handling = expression.args.get("null_handling")
3015        null_handling = f" {null_handling}" if null_handling else ""
3016        return_type = self.sql(expression, "return_type")
3017        return_type = f" RETURNING {return_type}" if return_type else ""
3018        strict = " STRICT" if expression.args.get("strict") else ""
3019        return self.func(
3020            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
3021        )
3022
3023    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
3024        this = self.sql(expression, "this")
3025        order = self.sql(expression, "order")
3026        null_handling = expression.args.get("null_handling")
3027        null_handling = f" {null_handling}" if null_handling else ""
3028        return_type = self.sql(expression, "return_type")
3029        return_type = f" RETURNING {return_type}" if return_type else ""
3030        strict = " STRICT" if expression.args.get("strict") else ""
3031        return self.func(
3032            "JSON_ARRAYAGG",
3033            this,
3034            suffix=f"{order}{null_handling}{return_type}{strict})",
3035        )
3036
3037    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
3038        path = self.sql(expression, "path")
3039        path = f" PATH {path}" if path else ""
3040        nested_schema = self.sql(expression, "nested_schema")
3041
3042        if nested_schema:
3043            return f"NESTED{path} {nested_schema}"
3044
3045        this = self.sql(expression, "this")
3046        kind = self.sql(expression, "kind")
3047        kind = f" {kind}" if kind else ""
3048        return f"{this}{kind}{path}"
3049
3050    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
3051        return self.func("COLUMNS", *expression.expressions)
3052
3053    def jsontable_sql(self, expression: exp.JSONTable) -> str:
3054        this = self.sql(expression, "this")
3055        path = self.sql(expression, "path")
3056        path = f", {path}" if path else ""
3057        error_handling = expression.args.get("error_handling")
3058        error_handling = f" {error_handling}" if error_handling else ""
3059        empty_handling = expression.args.get("empty_handling")
3060        empty_handling = f" {empty_handling}" if empty_handling else ""
3061        schema = self.sql(expression, "schema")
3062        return self.func(
3063            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
3064        )
3065
3066    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
3067        this = self.sql(expression, "this")
3068        kind = self.sql(expression, "kind")
3069        path = self.sql(expression, "path")
3070        path = f" {path}" if path else ""
3071        as_json = " AS JSON" if expression.args.get("as_json") else ""
3072        return f"{this} {kind}{path}{as_json}"
3073
3074    def openjson_sql(self, expression: exp.OpenJSON) -> str:
3075        this = self.sql(expression, "this")
3076        path = self.sql(expression, "path")
3077        path = f", {path}" if path else ""
3078        expressions = self.expressions(expression)
3079        with_ = (
3080            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
3081            if expressions
3082            else ""
3083        )
3084        return f"OPENJSON({this}{path}){with_}"
3085
3086    def in_sql(self, expression: exp.In) -> str:
3087        query = expression.args.get("query")
3088        unnest = expression.args.get("unnest")
3089        field = expression.args.get("field")
3090        is_global = " GLOBAL" if expression.args.get("is_global") else ""
3091
3092        if query:
3093            in_sql = self.sql(query)
3094        elif unnest:
3095            in_sql = self.in_unnest_op(unnest)
3096        elif field:
3097            in_sql = self.sql(field)
3098        else:
3099            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
3100
3101        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
3102
3103    def in_unnest_op(self, unnest: exp.Unnest) -> str:
3104        return f"(SELECT {self.sql(unnest)})"
3105
3106    def interval_sql(self, expression: exp.Interval) -> str:
3107        unit = self.sql(expression, "unit")
3108        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
3109            unit = self.TIME_PART_SINGULARS.get(unit, unit)
3110        unit = f" {unit}" if unit else ""
3111
3112        if self.SINGLE_STRING_INTERVAL:
3113            this = expression.this.name if expression.this else ""
3114            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
3115
3116        this = self.sql(expression, "this")
3117        if this:
3118            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
3119            this = f" {this}" if unwrapped else f" ({this})"
3120
3121        return f"INTERVAL{this}{unit}"
3122
3123    def return_sql(self, expression: exp.Return) -> str:
3124        return f"RETURN {self.sql(expression, 'this')}"
3125
3126    def reference_sql(self, expression: exp.Reference) -> str:
3127        this = self.sql(expression, "this")
3128        expressions = self.expressions(expression, flat=True)
3129        expressions = f"({expressions})" if expressions else ""
3130        options = self.expressions(expression, key="options", flat=True, sep=" ")
3131        options = f" {options}" if options else ""
3132        return f"REFERENCES {this}{expressions}{options}"
3133
3134    def anonymous_sql(self, expression: exp.Anonymous) -> str:
3135        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
3136        parent = expression.parent
3137        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
3138        return self.func(
3139            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
3140        )
3141
3142    def paren_sql(self, expression: exp.Paren) -> str:
3143        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
3144        return f"({sql}{self.seg(')', sep='')}"
3145
3146    def neg_sql(self, expression: exp.Neg) -> str:
3147        # This makes sure we don't convert "- - 5" to "--5", which is a comment
3148        this_sql = self.sql(expression, "this")
3149        sep = " " if this_sql[0] == "-" else ""
3150        return f"-{sep}{this_sql}"
3151
3152    def not_sql(self, expression: exp.Not) -> str:
3153        return f"NOT {self.sql(expression, 'this')}"
3154
3155    def alias_sql(self, expression: exp.Alias) -> str:
3156        alias = self.sql(expression, "alias")
3157        alias = f" AS {alias}" if alias else ""
3158        return f"{self.sql(expression, 'this')}{alias}"
3159
3160    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
3161        alias = expression.args["alias"]
3162
3163        parent = expression.parent
3164        pivot = parent and parent.parent
3165
3166        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
3167            identifier_alias = isinstance(alias, exp.Identifier)
3168            literal_alias = isinstance(alias, exp.Literal)
3169
3170            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3171                alias.replace(exp.Literal.string(alias.output_name))
3172            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3173                alias.replace(exp.to_identifier(alias.output_name))
3174
3175        return self.alias_sql(expression)
3176
3177    def aliases_sql(self, expression: exp.Aliases) -> str:
3178        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
3179
3180    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
3181        this = self.sql(expression, "this")
3182        index = self.sql(expression, "expression")
3183        return f"{this} AT {index}"
3184
3185    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
3186        this = self.sql(expression, "this")
3187        zone = self.sql(expression, "zone")
3188        return f"{this} AT TIME ZONE {zone}"
3189
3190    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
3191        this = self.sql(expression, "this")
3192        zone = self.sql(expression, "zone")
3193        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
3194
3195    def add_sql(self, expression: exp.Add) -> str:
3196        return self.binary(expression, "+")
3197
3198    def and_sql(
3199        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
3200    ) -> str:
3201        return self.connector_sql(expression, "AND", stack)
3202
3203    def or_sql(
3204        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
3205    ) -> str:
3206        return self.connector_sql(expression, "OR", stack)
3207
3208    def xor_sql(
3209        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
3210    ) -> str:
3211        return self.connector_sql(expression, "XOR", stack)
3212
3213    def connector_sql(
3214        self,
3215        expression: exp.Connector,
3216        op: str,
3217        stack: t.Optional[t.List[str | exp.Expression]] = None,
3218    ) -> str:
3219        if stack is not None:
3220            if expression.expressions:
3221                stack.append(self.expressions(expression, sep=f" {op} "))
3222            else:
3223                stack.append(expression.right)
3224                if expression.comments and self.comments:
3225                    for comment in expression.comments:
3226                        if comment:
3227                            op += f" /*{self.pad_comment(comment)}*/"
3228                stack.extend((op, expression.left))
3229            return op
3230
3231        stack = [expression]
3232        sqls: t.List[str] = []
3233        ops = set()
3234
3235        while stack:
3236            node = stack.pop()
3237            if isinstance(node, exp.Connector):
3238                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
3239            else:
3240                sql = self.sql(node)
3241                if sqls and sqls[-1] in ops:
3242                    sqls[-1] += f" {sql}"
3243                else:
3244                    sqls.append(sql)
3245
3246        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
3247        return sep.join(sqls)
3248
3249    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
3250        return self.binary(expression, "&")
3251
3252    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
3253        return self.binary(expression, "<<")
3254
3255    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
3256        return f"~{self.sql(expression, 'this')}"
3257
3258    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
3259        return self.binary(expression, "|")
3260
3261    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
3262        return self.binary(expression, ">>")
3263
3264    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
3265        return self.binary(expression, "^")
3266
3267    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
3268        format_sql = self.sql(expression, "format")
3269        format_sql = f" FORMAT {format_sql}" if format_sql else ""
3270        to_sql = self.sql(expression, "to")
3271        to_sql = f" {to_sql}" if to_sql else ""
3272        action = self.sql(expression, "action")
3273        action = f" {action}" if action else ""
3274        default = self.sql(expression, "default")
3275        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
3276        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
3277
3278    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
3279        zone = self.sql(expression, "this")
3280        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
3281
3282    def collate_sql(self, expression: exp.Collate) -> str:
3283        if self.COLLATE_IS_FUNC:
3284            return self.function_fallback_sql(expression)
3285        return self.binary(expression, "COLLATE")
3286
3287    def command_sql(self, expression: exp.Command) -> str:
3288        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
3289
3290    def comment_sql(self, expression: exp.Comment) -> str:
3291        this = self.sql(expression, "this")
3292        kind = expression.args["kind"]
3293        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
3294        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
3295        expression_sql = self.sql(expression, "expression")
3296        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
3297
3298    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
3299        this = self.sql(expression, "this")
3300        delete = " DELETE" if expression.args.get("delete") else ""
3301        recompress = self.sql(expression, "recompress")
3302        recompress = f" RECOMPRESS {recompress}" if recompress else ""
3303        to_disk = self.sql(expression, "to_disk")
3304        to_disk = f" TO DISK {to_disk}" if to_disk else ""
3305        to_volume = self.sql(expression, "to_volume")
3306        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
3307        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
3308
3309    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
3310        where = self.sql(expression, "where")
3311        group = self.sql(expression, "group")
3312        aggregates = self.expressions(expression, key="aggregates")
3313        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
3314
3315        if not (where or group or aggregates) and len(expression.expressions) == 1:
3316            return f"TTL {self.expressions(expression, flat=True)}"
3317
3318        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
3319
3320    def transaction_sql(self, expression: exp.Transaction) -> str:
3321        return "BEGIN"
3322
3323    def commit_sql(self, expression: exp.Commit) -> str:
3324        chain = expression.args.get("chain")
3325        if chain is not None:
3326            chain = " AND CHAIN" if chain else " AND NO CHAIN"
3327
3328        return f"COMMIT{chain or ''}"
3329
3330    def rollback_sql(self, expression: exp.Rollback) -> str:
3331        savepoint = expression.args.get("savepoint")
3332        savepoint = f" TO {savepoint}" if savepoint else ""
3333        return f"ROLLBACK{savepoint}"
3334
3335    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
3336        this = self.sql(expression, "this")
3337
3338        dtype = self.sql(expression, "dtype")
3339        if dtype:
3340            collate = self.sql(expression, "collate")
3341            collate = f" COLLATE {collate}" if collate else ""
3342            using = self.sql(expression, "using")
3343            using = f" USING {using}" if using else ""
3344            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
3345
3346        default = self.sql(expression, "default")
3347        if default:
3348            return f"ALTER COLUMN {this} SET DEFAULT {default}"
3349
3350        comment = self.sql(expression, "comment")
3351        if comment:
3352            return f"ALTER COLUMN {this} COMMENT {comment}"
3353
3354        visible = expression.args.get("visible")
3355        if visible:
3356            return f"ALTER COLUMN {this} SET {visible}"
3357
3358        allow_null = expression.args.get("allow_null")
3359        drop = expression.args.get("drop")
3360
3361        if not drop and not allow_null:
3362            self.unsupported("Unsupported ALTER COLUMN syntax")
3363
3364        if allow_null is not None:
3365            keyword = "DROP" if drop else "SET"
3366            return f"ALTER COLUMN {this} {keyword} NOT NULL"
3367
3368        return f"ALTER COLUMN {this} DROP DEFAULT"
3369
3370    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3371        this = self.sql(expression, "this")
3372
3373        visible = expression.args.get("visible")
3374        visible_sql = "VISIBLE" if visible else "INVISIBLE"
3375
3376        return f"ALTER INDEX {this} {visible_sql}"
3377
3378    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
3379        this = self.sql(expression, "this")
3380        if not isinstance(expression.this, exp.Var):
3381            this = f"KEY DISTKEY {this}"
3382        return f"ALTER DISTSTYLE {this}"
3383
3384    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
3385        compound = " COMPOUND" if expression.args.get("compound") else ""
3386        this = self.sql(expression, "this")
3387        expressions = self.expressions(expression, flat=True)
3388        expressions = f"({expressions})" if expressions else ""
3389        return f"ALTER{compound} SORTKEY {this or expressions}"
3390
3391    def alterrename_sql(self, expression: exp.AlterRename) -> str:
3392        if not self.RENAME_TABLE_WITH_DB:
3393            # Remove db from tables
3394            expression = expression.transform(
3395                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3396            ).assert_is(exp.AlterRename)
3397        this = self.sql(expression, "this")
3398        return f"RENAME TO {this}"
3399
3400    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3401        exists = " IF EXISTS" if expression.args.get("exists") else ""
3402        old_column = self.sql(expression, "this")
3403        new_column = self.sql(expression, "to")
3404        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
3405
3406    def alterset_sql(self, expression: exp.AlterSet) -> str:
3407        exprs = self.expressions(expression, flat=True)
3408        return f"SET {exprs}"
3409
3410    def alter_sql(self, expression: exp.Alter) -> str:
3411        actions = expression.args["actions"]
3412
3413        if isinstance(actions[0], exp.ColumnDef):
3414            actions = self.add_column_sql(expression)
3415        elif isinstance(actions[0], exp.Schema):
3416            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
3417        elif isinstance(actions[0], exp.Delete):
3418            actions = self.expressions(expression, key="actions", flat=True)
3419        elif isinstance(actions[0], exp.Query):
3420            actions = "AS " + self.expressions(expression, key="actions")
3421        else:
3422            actions = self.expressions(expression, key="actions", flat=True)
3423
3424        exists = " IF EXISTS" if expression.args.get("exists") else ""
3425        on_cluster = self.sql(expression, "cluster")
3426        on_cluster = f" {on_cluster}" if on_cluster else ""
3427        only = " ONLY" if expression.args.get("only") else ""
3428        options = self.expressions(expression, key="options")
3429        options = f", {options}" if options else ""
3430        kind = self.sql(expression, "kind")
3431        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3432
3433        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
3434
3435    def add_column_sql(self, expression: exp.Alter) -> str:
3436        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
3437            return self.expressions(
3438                expression,
3439                key="actions",
3440                prefix="ADD COLUMN ",
3441                skip_first=True,
3442            )
3443        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
3444
3445    def droppartition_sql(self, expression: exp.DropPartition) -> str:
3446        expressions = self.expressions(expression)
3447        exists = " IF EXISTS " if expression.args.get("exists") else " "
3448        return f"DROP{exists}{expressions}"
3449
3450    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
3451        return f"ADD {self.expressions(expression)}"
3452
3453    def distinct_sql(self, expression: exp.Distinct) -> str:
3454        this = self.expressions(expression, flat=True)
3455
3456        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
3457            case = exp.case()
3458            for arg in expression.expressions:
3459                case = case.when(arg.is_(exp.null()), exp.null())
3460            this = self.sql(case.else_(f"({this})"))
3461
3462        this = f" {this}" if this else ""
3463
3464        on = self.sql(expression, "on")
3465        on = f" ON {on}" if on else ""
3466        return f"DISTINCT{this}{on}"
3467
3468    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
3469        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
3470
3471    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
3472        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
3473
3474    def havingmax_sql(self, expression: exp.HavingMax) -> str:
3475        this_sql = self.sql(expression, "this")
3476        expression_sql = self.sql(expression, "expression")
3477        kind = "MAX" if expression.args.get("max") else "MIN"
3478        return f"{this_sql} HAVING {kind} {expression_sql}"
3479
3480    def intdiv_sql(self, expression: exp.IntDiv) -> str:
3481        return self.sql(
3482            exp.Cast(
3483                this=exp.Div(this=expression.this, expression=expression.expression),
3484                to=exp.DataType(this=exp.DataType.Type.INT),
3485            )
3486        )
3487
3488    def dpipe_sql(self, expression: exp.DPipe) -> str:
3489        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
3490            return self.func(
3491                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
3492            )
3493        return self.binary(expression, "||")
3494
3495    def div_sql(self, expression: exp.Div) -> str:
3496        l, r = expression.left, expression.right
3497
3498        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
3499            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
3500
3501        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
3502            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
3503                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
3504
3505        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
3506            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
3507                return self.sql(
3508                    exp.cast(
3509                        l / r,
3510                        to=exp.DataType.Type.BIGINT,
3511                    )
3512                )
3513
3514        return self.binary(expression, "/")
3515
3516    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
3517        n = exp._wrap(expression.this, exp.Binary)
3518        d = exp._wrap(expression.expression, exp.Binary)
3519        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
3520
3521    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3522        return self.binary(expression, "OVERLAPS")
3523
3524    def distance_sql(self, expression: exp.Distance) -> str:
3525        return self.binary(expression, "<->")
3526
3527    def dot_sql(self, expression: exp.Dot) -> str:
3528        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
3529
3530    def eq_sql(self, expression: exp.EQ) -> str:
3531        return self.binary(expression, "=")
3532
3533    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3534        return self.binary(expression, ":=")
3535
3536    def escape_sql(self, expression: exp.Escape) -> str:
3537        return self.binary(expression, "ESCAPE")
3538
3539    def glob_sql(self, expression: exp.Glob) -> str:
3540        return self.binary(expression, "GLOB")
3541
3542    def gt_sql(self, expression: exp.GT) -> str:
3543        return self.binary(expression, ">")
3544
3545    def gte_sql(self, expression: exp.GTE) -> str:
3546        return self.binary(expression, ">=")
3547
3548    def ilike_sql(self, expression: exp.ILike) -> str:
3549        return self.binary(expression, "ILIKE")
3550
3551    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
3552        return self.binary(expression, "ILIKE ANY")
3553
3554    def is_sql(self, expression: exp.Is) -> str:
3555        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3556            return self.sql(
3557                expression.this if expression.expression.this else exp.not_(expression.this)
3558            )
3559        return self.binary(expression, "IS")
3560
3561    def like_sql(self, expression: exp.Like) -> str:
3562        return self.binary(expression, "LIKE")
3563
3564    def likeany_sql(self, expression: exp.LikeAny) -> str:
3565        return self.binary(expression, "LIKE ANY")
3566
3567    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3568        return self.binary(expression, "SIMILAR TO")
3569
3570    def lt_sql(self, expression: exp.LT) -> str:
3571        return self.binary(expression, "<")
3572
3573    def lte_sql(self, expression: exp.LTE) -> str:
3574        return self.binary(expression, "<=")
3575
3576    def mod_sql(self, expression: exp.Mod) -> str:
3577        return self.binary(expression, "%")
3578
3579    def mul_sql(self, expression: exp.Mul) -> str:
3580        return self.binary(expression, "*")
3581
3582    def neq_sql(self, expression: exp.NEQ) -> str:
3583        return self.binary(expression, "<>")
3584
3585    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3586        return self.binary(expression, "IS NOT DISTINCT FROM")
3587
3588    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3589        return self.binary(expression, "IS DISTINCT FROM")
3590
3591    def slice_sql(self, expression: exp.Slice) -> str:
3592        return self.binary(expression, ":")
3593
3594    def sub_sql(self, expression: exp.Sub) -> str:
3595        return self.binary(expression, "-")
3596
3597    def trycast_sql(self, expression: exp.TryCast) -> str:
3598        return self.cast_sql(expression, safe_prefix="TRY_")
3599
3600    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
3601        return self.cast_sql(expression)
3602
3603    def try_sql(self, expression: exp.Try) -> str:
3604        if not self.TRY_SUPPORTED:
3605            self.unsupported("Unsupported TRY function")
3606            return self.sql(expression, "this")
3607
3608        return self.func("TRY", expression.this)
3609
3610    def log_sql(self, expression: exp.Log) -> str:
3611        this = expression.this
3612        expr = expression.expression
3613
3614        if self.dialect.LOG_BASE_FIRST is False:
3615            this, expr = expr, this
3616        elif self.dialect.LOG_BASE_FIRST is None and expr:
3617            if this.name in ("2", "10"):
3618                return self.func(f"LOG{this.name}", expr)
3619
3620            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
3621
3622        return self.func("LOG", this, expr)
3623
3624    def use_sql(self, expression: exp.Use) -> str:
3625        kind = self.sql(expression, "kind")
3626        kind = f" {kind}" if kind else ""
3627        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
3628        this = f" {this}" if this else ""
3629        return f"USE{kind}{this}"
3630
3631    def binary(self, expression: exp.Binary, op: str) -> str:
3632        sqls: t.List[str] = []
3633        stack: t.List[t.Union[str, exp.Expression]] = [expression]
3634        binary_type = type(expression)
3635
3636        while stack:
3637            node = stack.pop()
3638
3639            if type(node) is binary_type:
3640                op_func = node.args.get("operator")
3641                if op_func:
3642                    op = f"OPERATOR({self.sql(op_func)})"
3643
3644                stack.append(node.right)
3645                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
3646                stack.append(node.left)
3647            else:
3648                sqls.append(self.sql(node))
3649
3650        return "".join(sqls)
3651
3652    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
3653        to_clause = self.sql(expression, "to")
3654        if to_clause:
3655            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
3656
3657        return self.function_fallback_sql(expression)
3658
3659    def function_fallback_sql(self, expression: exp.Func) -> str:
3660        args = []
3661
3662        for key in expression.arg_types:
3663            arg_value = expression.args.get(key)
3664
3665            if isinstance(arg_value, list):
3666                for value in arg_value:
3667                    args.append(value)
3668            elif arg_value is not None:
3669                args.append(arg_value)
3670
3671        if self.dialect.PRESERVE_ORIGINAL_NAMES:
3672            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3673        else:
3674            name = expression.sql_name()
3675
3676        return self.func(name, *args)
3677
3678    def func(
3679        self,
3680        name: str,
3681        *args: t.Optional[exp.Expression | str],
3682        prefix: str = "(",
3683        suffix: str = ")",
3684        normalize: bool = True,
3685    ) -> str:
3686        name = self.normalize_func(name) if normalize else name
3687        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
3688
3689    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
3690        arg_sqls = tuple(
3691            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
3692        )
3693        if self.pretty and self.too_wide(arg_sqls):
3694            return self.indent(
3695                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
3696            )
3697        return sep.join(arg_sqls)
3698
3699    def too_wide(self, args: t.Iterable) -> bool:
3700        return sum(len(arg) for arg in args) > self.max_text_width
3701
3702    def format_time(
3703        self,
3704        expression: exp.Expression,
3705        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
3706        inverse_time_trie: t.Optional[t.Dict] = None,
3707    ) -> t.Optional[str]:
3708        return format_time(
3709            self.sql(expression, "format"),
3710            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
3711            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
3712        )
3713
3714    def expressions(
3715        self,
3716        expression: t.Optional[exp.Expression] = None,
3717        key: t.Optional[str] = None,
3718        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3719        flat: bool = False,
3720        indent: bool = True,
3721        skip_first: bool = False,
3722        skip_last: bool = False,
3723        sep: str = ", ",
3724        prefix: str = "",
3725        dynamic: bool = False,
3726        new_line: bool = False,
3727    ) -> str:
3728        expressions = expression.args.get(key or "expressions") if expression else sqls
3729
3730        if not expressions:
3731            return ""
3732
3733        if flat:
3734            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3735
3736        num_sqls = len(expressions)
3737        result_sqls = []
3738
3739        for i, e in enumerate(expressions):
3740            sql = self.sql(e, comment=False)
3741            if not sql:
3742                continue
3743
3744            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3745
3746            if self.pretty:
3747                if self.leading_comma:
3748                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
3749                else:
3750                    result_sqls.append(
3751                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
3752                    )
3753            else:
3754                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3755
3756        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
3757            if new_line:
3758                result_sqls.insert(0, "")
3759                result_sqls.append("")
3760            result_sql = "\n".join(s.rstrip() for s in result_sqls)
3761        else:
3762            result_sql = "".join(result_sqls)
3763
3764        return (
3765            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
3766            if indent
3767            else result_sql
3768        )
3769
3770    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3771        flat = flat or isinstance(expression.parent, exp.Properties)
3772        expressions_sql = self.expressions(expression, flat=flat)
3773        if flat:
3774            return f"{op} {expressions_sql}"
3775        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3776
3777    def naked_property(self, expression: exp.Property) -> str:
3778        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3779        if not property_name:
3780            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3781        return f"{property_name} {self.sql(expression, 'this')}"
3782
3783    def tag_sql(self, expression: exp.Tag) -> str:
3784        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3785
3786    def token_sql(self, token_type: TokenType) -> str:
3787        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3788
3789    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3790        this = self.sql(expression, "this")
3791        expressions = self.no_identify(self.expressions, expression)
3792        expressions = (
3793            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3794        )
3795        return f"{this}{expressions}" if expressions.strip() != "" else this
3796
3797    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3798        this = self.sql(expression, "this")
3799        expressions = self.expressions(expression, flat=True)
3800        return f"{this}({expressions})"
3801
3802    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3803        return self.binary(expression, "=>")
3804
3805    def when_sql(self, expression: exp.When) -> str:
3806        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3807        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3808        condition = self.sql(expression, "condition")
3809        condition = f" AND {condition}" if condition else ""
3810
3811        then_expression = expression.args.get("then")
3812        if isinstance(then_expression, exp.Insert):
3813            this = self.sql(then_expression, "this")
3814            this = f"INSERT {this}" if this else "INSERT"
3815            then = self.sql(then_expression, "expression")
3816            then = f"{this} VALUES {then}" if then else this
3817        elif isinstance(then_expression, exp.Update):
3818            if isinstance(then_expression.args.get("expressions"), exp.Star):
3819                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3820            else:
3821                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3822        else:
3823            then = self.sql(then_expression)
3824        return f"WHEN {matched}{source}{condition} THEN {then}"
3825
3826    def whens_sql(self, expression: exp.Whens) -> str:
3827        return self.expressions(expression, sep=" ", indent=False)
3828
3829    def merge_sql(self, expression: exp.Merge) -> str:
3830        table = expression.this
3831        table_alias = ""
3832
3833        hints = table.args.get("hints")
3834        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3835            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3836            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3837
3838        this = self.sql(table)
3839        using = f"USING {self.sql(expression, 'using')}"
3840        on = f"ON {self.sql(expression, 'on')}"
3841        whens = self.sql(expression, "whens")
3842
3843        returning = self.sql(expression, "returning")
3844        if returning:
3845            whens = f"{whens}{returning}"
3846
3847        sep = self.sep()
3848
3849        return self.prepend_ctes(
3850            expression,
3851            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
3852        )
3853
3854    @unsupported_args("format")
3855    def tochar_sql(self, expression: exp.ToChar) -> str:
3856        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
3857
3858    def tonumber_sql(self, expression: exp.ToNumber) -> str:
3859        if not self.SUPPORTS_TO_NUMBER:
3860            self.unsupported("Unsupported TO_NUMBER function")
3861            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
3862
3863        fmt = expression.args.get("format")
3864        if not fmt:
3865            self.unsupported("Conversion format is required for TO_NUMBER")
3866            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
3867
3868        return self.func("TO_NUMBER", expression.this, fmt)
3869
3870    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3871        this = self.sql(expression, "this")
3872        kind = self.sql(expression, "kind")
3873        settings_sql = self.expressions(expression, key="settings", sep=" ")
3874        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3875        return f"{this}({kind}{args})"
3876
3877    def dictrange_sql(self, expression: exp.DictRange) -> str:
3878        this = self.sql(expression, "this")
3879        max = self.sql(expression, "max")
3880        min = self.sql(expression, "min")
3881        return f"{this}(MIN {min} MAX {max})"
3882
3883    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3884        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
3885
3886    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
3887        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
3888
3889    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
3890    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
3891        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
3892
3893    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
3894    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
3895        expressions = self.expressions(expression, flat=True)
3896        expressions = f" {self.wrap(expressions)}" if expressions else ""
3897        buckets = self.sql(expression, "buckets")
3898        kind = self.sql(expression, "kind")
3899        buckets = f" BUCKETS {buckets}" if buckets else ""
3900        order = self.sql(expression, "order")
3901        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
3902
3903    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3904        return ""
3905
3906    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3907        expressions = self.expressions(expression, key="expressions", flat=True)
3908        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3909        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3910        buckets = self.sql(expression, "buckets")
3911        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3912
3913    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3914        this = self.sql(expression, "this")
3915        having = self.sql(expression, "having")
3916
3917        if having:
3918            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3919
3920        return self.func("ANY_VALUE", this)
3921
3922    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3923        transform = self.func("TRANSFORM", *expression.expressions)
3924        row_format_before = self.sql(expression, "row_format_before")
3925        row_format_before = f" {row_format_before}" if row_format_before else ""
3926        record_writer = self.sql(expression, "record_writer")
3927        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3928        using = f" USING {self.sql(expression, 'command_script')}"
3929        schema = self.sql(expression, "schema")
3930        schema = f" AS {schema}" if schema else ""
3931        row_format_after = self.sql(expression, "row_format_after")
3932        row_format_after = f" {row_format_after}" if row_format_after else ""
3933        record_reader = self.sql(expression, "record_reader")
3934        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3935        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3936
3937    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3938        key_block_size = self.sql(expression, "key_block_size")
3939        if key_block_size:
3940            return f"KEY_BLOCK_SIZE = {key_block_size}"
3941
3942        using = self.sql(expression, "using")
3943        if using:
3944            return f"USING {using}"
3945
3946        parser = self.sql(expression, "parser")
3947        if parser:
3948            return f"WITH PARSER {parser}"
3949
3950        comment = self.sql(expression, "comment")
3951        if comment:
3952            return f"COMMENT {comment}"
3953
3954        visible = expression.args.get("visible")
3955        if visible is not None:
3956            return "VISIBLE" if visible else "INVISIBLE"
3957
3958        engine_attr = self.sql(expression, "engine_attr")
3959        if engine_attr:
3960            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3961
3962        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3963        if secondary_engine_attr:
3964            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3965
3966        self.unsupported("Unsupported index constraint option.")
3967        return ""
3968
3969    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3970        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3971        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
3972
3973    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3974        kind = self.sql(expression, "kind")
3975        kind = f"{kind} INDEX" if kind else "INDEX"
3976        this = self.sql(expression, "this")
3977        this = f" {this}" if this else ""
3978        index_type = self.sql(expression, "index_type")
3979        index_type = f" USING {index_type}" if index_type else ""
3980        expressions = self.expressions(expression, flat=True)
3981        expressions = f" ({expressions})" if expressions else ""
3982        options = self.expressions(expression, key="options", sep=" ")
3983        options = f" {options}" if options else ""
3984        return f"{kind}{this}{index_type}{expressions}{options}"
3985
3986    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3987        if self.NVL2_SUPPORTED:
3988            return self.function_fallback_sql(expression)
3989
3990        case = exp.Case().when(
3991            expression.this.is_(exp.null()).not_(copy=False),
3992            expression.args["true"],
3993            copy=False,
3994        )
3995        else_cond = expression.args.get("false")
3996        if else_cond:
3997            case.else_(else_cond, copy=False)
3998
3999        return self.sql(case)
4000
4001    def comprehension_sql(self, expression: exp.Comprehension) -> str:
4002        this = self.sql(expression, "this")
4003        expr = self.sql(expression, "expression")
4004        iterator = self.sql(expression, "iterator")
4005        condition = self.sql(expression, "condition")
4006        condition = f" IF {condition}" if condition else ""
4007        return f"{this} FOR {expr} IN {iterator}{condition}"
4008
4009    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
4010        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
4011
4012    def opclass_sql(self, expression: exp.Opclass) -> str:
4013        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
4014
4015    def predict_sql(self, expression: exp.Predict) -> str:
4016        model = self.sql(expression, "this")
4017        model = f"MODEL {model}"
4018        table = self.sql(expression, "expression")
4019        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4020        parameters = self.sql(expression, "params_struct")
4021        return self.func("PREDICT", model, table, parameters or None)
4022
4023    def forin_sql(self, expression: exp.ForIn) -> str:
4024        this = self.sql(expression, "this")
4025        expression_sql = self.sql(expression, "expression")
4026        return f"FOR {this} DO {expression_sql}"
4027
4028    def refresh_sql(self, expression: exp.Refresh) -> str:
4029        this = self.sql(expression, "this")
4030        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
4031        return f"REFRESH {table}{this}"
4032
4033    def toarray_sql(self, expression: exp.ToArray) -> str:
4034        arg = expression.this
4035        if not arg.type:
4036            from sqlglot.optimizer.annotate_types import annotate_types
4037
4038            arg = annotate_types(arg, dialect=self.dialect)
4039
4040        if arg.is_type(exp.DataType.Type.ARRAY):
4041            return self.sql(arg)
4042
4043        cond_for_null = arg.is_(exp.null())
4044        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
4045
4046    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
4047        this = expression.this
4048        time_format = self.format_time(expression)
4049
4050        if time_format:
4051            return self.sql(
4052                exp.cast(
4053                    exp.StrToTime(this=this, format=expression.args["format"]),
4054                    exp.DataType.Type.TIME,
4055                )
4056            )
4057
4058        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
4059            return self.sql(this)
4060
4061        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
4062
4063    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
4064        this = expression.this
4065        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
4066            return self.sql(this)
4067
4068        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
4069
4070    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
4071        this = expression.this
4072        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
4073            return self.sql(this)
4074
4075        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
4076
4077    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
4078        this = expression.this
4079        time_format = self.format_time(expression)
4080
4081        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
4082            return self.sql(
4083                exp.cast(
4084                    exp.StrToTime(this=this, format=expression.args["format"]),
4085                    exp.DataType.Type.DATE,
4086                )
4087            )
4088
4089        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
4090            return self.sql(this)
4091
4092        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
4093
4094    def unixdate_sql(self, expression: exp.UnixDate) -> str:
4095        return self.sql(
4096            exp.func(
4097                "DATEDIFF",
4098                expression.this,
4099                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
4100                "day",
4101            )
4102        )
4103
4104    def lastday_sql(self, expression: exp.LastDay) -> str:
4105        if self.LAST_DAY_SUPPORTS_DATE_PART:
4106            return self.function_fallback_sql(expression)
4107
4108        unit = expression.text("unit")
4109        if unit and unit != "MONTH":
4110            self.unsupported("Date parts are not supported in LAST_DAY.")
4111
4112        return self.func("LAST_DAY", expression.this)
4113
4114    def dateadd_sql(self, expression: exp.DateAdd) -> str:
4115        from sqlglot.dialects.dialect import unit_to_str
4116
4117        return self.func(
4118            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
4119        )
4120
4121    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
4122        if self.CAN_IMPLEMENT_ARRAY_ANY:
4123            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
4124            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
4125            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
4126            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
4127
4128        from sqlglot.dialects import Dialect
4129
4130        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
4131        if self.dialect.__class__ != Dialect:
4132            self.unsupported("ARRAY_ANY is unsupported")
4133
4134        return self.function_fallback_sql(expression)
4135
4136    def struct_sql(self, expression: exp.Struct) -> str:
4137        expression.set(
4138            "expressions",
4139            [
4140                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
4141                if isinstance(e, exp.PropertyEQ)
4142                else e
4143                for e in expression.expressions
4144            ],
4145        )
4146
4147        return self.function_fallback_sql(expression)
4148
4149    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
4150        low = self.sql(expression, "this")
4151        high = self.sql(expression, "expression")
4152
4153        return f"{low} TO {high}"
4154
4155    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
4156        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
4157        tables = f" {self.expressions(expression)}"
4158
4159        exists = " IF EXISTS" if expression.args.get("exists") else ""
4160
4161        on_cluster = self.sql(expression, "cluster")
4162        on_cluster = f" {on_cluster}" if on_cluster else ""
4163
4164        identity = self.sql(expression, "identity")
4165        identity = f" {identity} IDENTITY" if identity else ""
4166
4167        option = self.sql(expression, "option")
4168        option = f" {option}" if option else ""
4169
4170        partition = self.sql(expression, "partition")
4171        partition = f" {partition}" if partition else ""
4172
4173        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
4174
4175    # This transpiles T-SQL's CONVERT function
4176    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
4177    def convert_sql(self, expression: exp.Convert) -> str:
4178        to = expression.this
4179        value = expression.expression
4180        style = expression.args.get("style")
4181        safe = expression.args.get("safe")
4182        strict = expression.args.get("strict")
4183
4184        if not to or not value:
4185            return ""
4186
4187        # Retrieve length of datatype and override to default if not specified
4188        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4189            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
4190
4191        transformed: t.Optional[exp.Expression] = None
4192        cast = exp.Cast if strict else exp.TryCast
4193
4194        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
4195        if isinstance(style, exp.Literal) and style.is_int:
4196            from sqlglot.dialects.tsql import TSQL
4197
4198            style_value = style.name
4199            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
4200            if not converted_style:
4201                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
4202
4203            fmt = exp.Literal.string(converted_style)
4204
4205            if to.this == exp.DataType.Type.DATE:
4206                transformed = exp.StrToDate(this=value, format=fmt)
4207            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
4208                transformed = exp.StrToTime(this=value, format=fmt)
4209            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4210                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
4211            elif to.this == exp.DataType.Type.TEXT:
4212                transformed = exp.TimeToStr(this=value, format=fmt)
4213
4214        if not transformed:
4215            transformed = cast(this=value, to=to, safe=safe)
4216
4217        return self.sql(transformed)
4218
4219    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
4220        this = expression.this
4221        if isinstance(this, exp.JSONPathWildcard):
4222            this = self.json_path_part(this)
4223            return f".{this}" if this else ""
4224
4225        if exp.SAFE_IDENTIFIER_RE.match(this):
4226            return f".{this}"
4227
4228        this = self.json_path_part(this)
4229        return (
4230            f"[{this}]"
4231            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
4232            else f".{this}"
4233        )
4234
4235    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
4236        this = self.json_path_part(expression.this)
4237        return f"[{this}]" if this else ""
4238
4239    def _simplify_unless_literal(self, expression: E) -> E:
4240        if not isinstance(expression, exp.Literal):
4241            from sqlglot.optimizer.simplify import simplify
4242
4243            expression = simplify(expression, dialect=self.dialect)
4244
4245        return expression
4246
4247    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
4248        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
4249            # The first modifier here will be the one closest to the AggFunc's arg
4250            mods = sorted(
4251                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
4252                key=lambda x: 0
4253                if isinstance(x, exp.HavingMax)
4254                else (1 if isinstance(x, exp.Order) else 2),
4255            )
4256
4257            if mods:
4258                mod = mods[0]
4259                this = expression.__class__(this=mod.this.copy())
4260                this.meta["inline"] = True
4261                mod.this.replace(this)
4262                return self.sql(expression.this)
4263
4264            agg_func = expression.find(exp.AggFunc)
4265
4266            if agg_func:
4267                return self.sql(agg_func)[:-1] + f" {text})"
4268
4269        return f"{self.sql(expression, 'this')} {text}"
4270
4271    def _replace_line_breaks(self, string: str) -> str:
4272        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
4273        if self.pretty:
4274            return string.replace("\n", self.SENTINEL_LINE_BREAK)
4275        return string
4276
4277    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
4278        option = self.sql(expression, "this")
4279
4280        if expression.expressions:
4281            upper = option.upper()
4282
4283            # Snowflake FILE_FORMAT options are separated by whitespace
4284            sep = " " if upper == "FILE_FORMAT" else ", "
4285
4286            # Databricks copy/format options do not set their list of values with EQ
4287            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
4288            values = self.expressions(expression, flat=True, sep=sep)
4289            return f"{option}{op}({values})"
4290
4291        value = self.sql(expression, "expression")
4292
4293        if not value:
4294            return option
4295
4296        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
4297
4298        return f"{option}{op}{value}"
4299
4300    def credentials_sql(self, expression: exp.Credentials) -> str:
4301        cred_expr = expression.args.get("credentials")
4302        if isinstance(cred_expr, exp.Literal):
4303            # Redshift case: CREDENTIALS <string>
4304            credentials = self.sql(expression, "credentials")
4305            credentials = f"CREDENTIALS {credentials}" if credentials else ""
4306        else:
4307            # Snowflake case: CREDENTIALS = (...)
4308            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
4309            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
4310
4311        storage = self.sql(expression, "storage")
4312        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
4313
4314        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
4315        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
4316
4317        iam_role = self.sql(expression, "iam_role")
4318        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
4319
4320        region = self.sql(expression, "region")
4321        region = f" REGION {region}" if region else ""
4322
4323        return f"{credentials}{storage}{encryption}{iam_role}{region}"
4324
4325    def copy_sql(self, expression: exp.Copy) -> str:
4326        this = self.sql(expression, "this")
4327        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
4328
4329        credentials = self.sql(expression, "credentials")
4330        credentials = self.seg(credentials) if credentials else ""
4331        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
4332        files = self.expressions(expression, key="files", flat=True)
4333
4334        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
4335        params = self.expressions(
4336            expression,
4337            key="params",
4338            sep=sep,
4339            new_line=True,
4340            skip_last=True,
4341            skip_first=True,
4342            indent=self.COPY_PARAMS_ARE_WRAPPED,
4343        )
4344
4345        if params:
4346            if self.COPY_PARAMS_ARE_WRAPPED:
4347                params = f" WITH ({params})"
4348            elif not self.pretty:
4349                params = f" {params}"
4350
4351        return f"COPY{this}{kind} {files}{credentials}{params}"
4352
4353    def semicolon_sql(self, expression: exp.Semicolon) -> str:
4354        return ""
4355
4356    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
4357        on_sql = "ON" if expression.args.get("on") else "OFF"
4358        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
4359        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
4360        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
4361        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
4362
4363        if filter_col or retention_period:
4364            on_sql = self.func("ON", filter_col, retention_period)
4365
4366        return f"DATA_DELETION={on_sql}"
4367
4368    def maskingpolicycolumnconstraint_sql(
4369        self, expression: exp.MaskingPolicyColumnConstraint
4370    ) -> str:
4371        this = self.sql(expression, "this")
4372        expressions = self.expressions(expression, flat=True)
4373        expressions = f" USING ({expressions})" if expressions else ""
4374        return f"MASKING POLICY {this}{expressions}"
4375
4376    def gapfill_sql(self, expression: exp.GapFill) -> str:
4377        this = self.sql(expression, "this")
4378        this = f"TABLE {this}"
4379        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
4380
4381    def scope_resolution(self, rhs: str, scope_name: str) -> str:
4382        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
4383
4384    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
4385        this = self.sql(expression, "this")
4386        expr = expression.expression
4387
4388        if isinstance(expr, exp.Func):
4389            # T-SQL's CLR functions are case sensitive
4390            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
4391        else:
4392            expr = self.sql(expression, "expression")
4393
4394        return self.scope_resolution(expr, this)
4395
4396    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
4397        if self.PARSE_JSON_NAME is None:
4398            return self.sql(expression.this)
4399
4400        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
4401
4402    def rand_sql(self, expression: exp.Rand) -> str:
4403        lower = self.sql(expression, "lower")
4404        upper = self.sql(expression, "upper")
4405
4406        if lower and upper:
4407            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
4408        return self.func("RAND", expression.this)
4409
4410    def changes_sql(self, expression: exp.Changes) -> str:
4411        information = self.sql(expression, "information")
4412        information = f"INFORMATION => {information}"
4413        at_before = self.sql(expression, "at_before")
4414        at_before = f"{self.seg('')}{at_before}" if at_before else ""
4415        end = self.sql(expression, "end")
4416        end = f"{self.seg('')}{end}" if end else ""
4417
4418        return f"CHANGES ({information}){at_before}{end}"
4419
4420    def pad_sql(self, expression: exp.Pad) -> str:
4421        prefix = "L" if expression.args.get("is_left") else "R"
4422
4423        fill_pattern = self.sql(expression, "fill_pattern") or None
4424        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
4425            fill_pattern = "' '"
4426
4427        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
4428
4429    def summarize_sql(self, expression: exp.Summarize) -> str:
4430        table = " TABLE" if expression.args.get("table") else ""
4431        return f"SUMMARIZE{table} {self.sql(expression.this)}"
4432
4433    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
4434        generate_series = exp.GenerateSeries(**expression.args)
4435
4436        parent = expression.parent
4437        if isinstance(parent, (exp.Alias, exp.TableAlias)):
4438            parent = parent.parent
4439
4440        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
4441            return self.sql(exp.Unnest(expressions=[generate_series]))
4442
4443        if isinstance(parent, exp.Select):
4444            self.unsupported("GenerateSeries projection unnesting is not supported.")
4445
4446        return self.sql(generate_series)
4447
4448    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
4449        exprs = expression.expressions
4450        if not self.ARRAY_CONCAT_IS_VAR_LEN:
4451            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
4452        else:
4453            rhs = self.expressions(expression)
4454
4455        return self.func(name, expression.this, rhs or None)
4456
4457    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
4458        if self.SUPPORTS_CONVERT_TIMEZONE:
4459            return self.function_fallback_sql(expression)
4460
4461        source_tz = expression.args.get("source_tz")
4462        target_tz = expression.args.get("target_tz")
4463        timestamp = expression.args.get("timestamp")
4464
4465        if source_tz and timestamp:
4466            timestamp = exp.AtTimeZone(
4467                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
4468            )
4469
4470        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
4471
4472        return self.sql(expr)
4473
4474    def json_sql(self, expression: exp.JSON) -> str:
4475        this = self.sql(expression, "this")
4476        this = f" {this}" if this else ""
4477
4478        _with = expression.args.get("with")
4479
4480        if _with is None:
4481            with_sql = ""
4482        elif not _with:
4483            with_sql = " WITHOUT"
4484        else:
4485            with_sql = " WITH"
4486
4487        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4488
4489        return f"JSON{this}{with_sql}{unique_sql}"
4490
4491    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
4492        def _generate_on_options(arg: t.Any) -> str:
4493            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
4494
4495        path = self.sql(expression, "path")
4496        returning = self.sql(expression, "returning")
4497        returning = f" RETURNING {returning}" if returning else ""
4498
4499        on_condition = self.sql(expression, "on_condition")
4500        on_condition = f" {on_condition}" if on_condition else ""
4501
4502        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
4503
4504    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
4505        else_ = "ELSE " if expression.args.get("else_") else ""
4506        condition = self.sql(expression, "expression")
4507        condition = f"WHEN {condition} THEN " if condition else else_
4508        insert = self.sql(expression, "this")[len("INSERT") :].strip()
4509        return f"{condition}{insert}"
4510
4511    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
4512        kind = self.sql(expression, "kind")
4513        expressions = self.seg(self.expressions(expression, sep=" "))
4514        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
4515        return res
4516
4517    def oncondition_sql(self, expression: exp.OnCondition) -> str:
4518        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
4519        empty = expression.args.get("empty")
4520        empty = (
4521            f"DEFAULT {empty} ON EMPTY"
4522            if isinstance(empty, exp.Expression)
4523            else self.sql(expression, "empty")
4524        )
4525
4526        error = expression.args.get("error")
4527        error = (
4528            f"DEFAULT {error} ON ERROR"
4529            if isinstance(error, exp.Expression)
4530            else self.sql(expression, "error")
4531        )
4532
4533        if error and empty:
4534            error = (
4535                f"{empty} {error}"
4536                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
4537                else f"{error} {empty}"
4538            )
4539            empty = ""
4540
4541        null = self.sql(expression, "null")
4542
4543        return f"{empty}{error}{null}"
4544
4545    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
4546        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
4547        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
4548
4549    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
4550        this = self.sql(expression, "this")
4551        path = self.sql(expression, "path")
4552
4553        passing = self.expressions(expression, "passing")
4554        passing = f" PASSING {passing}" if passing else ""
4555
4556        on_condition = self.sql(expression, "on_condition")
4557        on_condition = f" {on_condition}" if on_condition else ""
4558
4559        path = f"{path}{passing}{on_condition}"
4560
4561        return self.func("JSON_EXISTS", this, path)
4562
4563    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
4564        array_agg = self.function_fallback_sql(expression)
4565
4566        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
4567        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
4568        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
4569            parent = expression.parent
4570            if isinstance(parent, exp.Filter):
4571                parent_cond = parent.expression.this
4572                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
4573            else:
4574                this = expression.this
4575                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
4576                if this.find(exp.Column):
4577                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
4578                    this_sql = (
4579                        self.expressions(this)
4580                        if isinstance(this, exp.Distinct)
4581                        else self.sql(expression, "this")
4582                    )
4583
4584                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
4585
4586        return array_agg
4587
4588    def apply_sql(self, expression: exp.Apply) -> str:
4589        this = self.sql(expression, "this")
4590        expr = self.sql(expression, "expression")
4591
4592        return f"{this} APPLY({expr})"
4593
4594    def grant_sql(self, expression: exp.Grant) -> str:
4595        privileges_sql = self.expressions(expression, key="privileges", flat=True)
4596
4597        kind = self.sql(expression, "kind")
4598        kind = f" {kind}" if kind else ""
4599
4600        securable = self.sql(expression, "securable")
4601        securable = f" {securable}" if securable else ""
4602
4603        principals = self.expressions(expression, key="principals", flat=True)
4604
4605        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
4606
4607        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
4608
4609    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
4610        this = self.sql(expression, "this")
4611        columns = self.expressions(expression, flat=True)
4612        columns = f"({columns})" if columns else ""
4613
4614        return f"{this}{columns}"
4615
4616    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
4617        this = self.sql(expression, "this")
4618
4619        kind = self.sql(expression, "kind")
4620        kind = f"{kind} " if kind else ""
4621
4622        return f"{kind}{this}"
4623
4624    def columns_sql(self, expression: exp.Columns):
4625        func = self.function_fallback_sql(expression)
4626        if expression.args.get("unpack"):
4627            func = f"*{func}"
4628
4629        return func
4630
4631    def overlay_sql(self, expression: exp.Overlay):
4632        this = self.sql(expression, "this")
4633        expr = self.sql(expression, "expression")
4634        from_sql = self.sql(expression, "from")
4635        for_sql = self.sql(expression, "for")
4636        for_sql = f" FOR {for_sql}" if for_sql else ""
4637
4638        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
4639
4640    @unsupported_args("format")
4641    def todouble_sql(self, expression: exp.ToDouble) -> str:
4642        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
4643
4644    def string_sql(self, expression: exp.String) -> str:
4645        this = expression.this
4646        zone = expression.args.get("zone")
4647
4648        if zone:
4649            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
4650            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
4651            # set for source_tz to transpile the time conversion before the STRING cast
4652            this = exp.ConvertTimezone(
4653                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
4654            )
4655
4656        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
4657
4658    def median_sql(self, expression: exp.Median):
4659        if not self.SUPPORTS_MEDIAN:
4660            return self.sql(
4661                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
4662            )
4663
4664        return self.function_fallback_sql(expression)
4665
4666    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
4667        filler = self.sql(expression, "this")
4668        filler = f" {filler}" if filler else ""
4669        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
4670        return f"TRUNCATE{filler} {with_count}"
4671
4672    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
4673        if self.SUPPORTS_UNIX_SECONDS:
4674            return self.function_fallback_sql(expression)
4675
4676        start_ts = exp.cast(
4677            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
4678        )
4679
4680        return self.sql(
4681            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
4682        )
4683
4684    def arraysize_sql(self, expression: exp.ArraySize) -> str:
4685        dim = expression.expression
4686
4687        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
4688        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
4689            if not (dim.is_int and dim.name == "1"):
4690                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
4691            dim = None
4692
4693        # If dimension is required but not specified, default initialize it
4694        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
4695            dim = exp.Literal.number(1)
4696
4697        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
4698
4699    def attach_sql(self, expression: exp.Attach) -> str:
4700        this = self.sql(expression, "this")
4701        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
4702        expressions = self.expressions(expression)
4703        expressions = f" ({expressions})" if expressions else ""
4704
4705        return f"ATTACH{exists_sql} {this}{expressions}"
4706
4707    def detach_sql(self, expression: exp.Detach) -> str:
4708        this = self.sql(expression, "this")
4709        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
4710
4711        return f"DETACH{exists_sql} {this}"
4712
4713    def attachoption_sql(self, expression: exp.AttachOption) -> str:
4714        this = self.sql(expression, "this")
4715        value = self.sql(expression, "expression")
4716        value = f" {value}" if value else ""
4717        return f"{this}{value}"
4718
4719    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
4720        this_sql = self.sql(expression, "this")
4721        if isinstance(expression.this, exp.Table):
4722            this_sql = f"TABLE {this_sql}"
4723
4724        return self.func(
4725            "FEATURES_AT_TIME",
4726            this_sql,
4727            expression.args.get("time"),
4728            expression.args.get("num_rows"),
4729            expression.args.get("ignore_feature_nulls"),
4730        )
4731
4732    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
4733        return (
4734            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
4735        )
4736
4737    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
4738        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
4739        encode = f"{encode} {self.sql(expression, 'this')}"
4740
4741        properties = expression.args.get("properties")
4742        if properties:
4743            encode = f"{encode} {self.properties(properties)}"
4744
4745        return encode
4746
4747    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
4748        this = self.sql(expression, "this")
4749        include = f"INCLUDE {this}"
4750
4751        column_def = self.sql(expression, "column_def")
4752        if column_def:
4753            include = f"{include} {column_def}"
4754
4755        alias = self.sql(expression, "alias")
4756        if alias:
4757            include = f"{include} AS {alias}"
4758
4759        return include
4760
4761    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
4762        name = f"NAME {self.sql(expression, 'this')}"
4763        return self.func("XMLELEMENT", name, *expression.expressions)
4764
4765    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
4766        partitions = self.expressions(expression, "partition_expressions")
4767        create = self.expressions(expression, "create_expressions")
4768        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
4769
4770    def partitionbyrangepropertydynamic_sql(
4771        self, expression: exp.PartitionByRangePropertyDynamic
4772    ) -> str:
4773        start = self.sql(expression, "start")
4774        end = self.sql(expression, "end")
4775
4776        every = expression.args["every"]
4777        if isinstance(every, exp.Interval) and every.this.is_string:
4778            every.this.replace(exp.Literal.number(every.name))
4779
4780        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
4781
4782    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
4783        name = self.sql(expression, "this")
4784        values = self.expressions(expression, flat=True)
4785
4786        return f"NAME {name} VALUE {values}"
4787
4788    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
4789        kind = self.sql(expression, "kind")
4790        sample = self.sql(expression, "sample")
4791        return f"SAMPLE {sample} {kind}"
4792
4793    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
4794        kind = self.sql(expression, "kind")
4795        option = self.sql(expression, "option")
4796        option = f" {option}" if option else ""
4797        this = self.sql(expression, "this")
4798        this = f" {this}" if this else ""
4799        columns = self.expressions(expression)
4800        columns = f" {columns}" if columns else ""
4801        return f"{kind}{option} STATISTICS{this}{columns}"
4802
4803    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
4804        this = self.sql(expression, "this")
4805        columns = self.expressions(expression)
4806        inner_expression = self.sql(expression, "expression")
4807        inner_expression = f" {inner_expression}" if inner_expression else ""
4808        update_options = self.sql(expression, "update_options")
4809        update_options = f" {update_options} UPDATE" if update_options else ""
4810        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
4811
4812    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
4813        kind = self.sql(expression, "kind")
4814        kind = f" {kind}" if kind else ""
4815        return f"DELETE{kind} STATISTICS"
4816
4817    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
4818        inner_expression = self.sql(expression, "expression")
4819        return f"LIST CHAINED ROWS{inner_expression}"
4820
4821    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
4822        kind = self.sql(expression, "kind")
4823        this = self.sql(expression, "this")
4824        this = f" {this}" if this else ""
4825        inner_expression = self.sql(expression, "expression")
4826        return f"VALIDATE {kind}{this}{inner_expression}"
4827
4828    def analyze_sql(self, expression: exp.Analyze) -> str:
4829        options = self.expressions(expression, key="options", sep=" ")
4830        options = f" {options}" if options else ""
4831        kind = self.sql(expression, "kind")
4832        kind = f" {kind}" if kind else ""
4833        this = self.sql(expression, "this")
4834        this = f" {this}" if this else ""
4835        mode = self.sql(expression, "mode")
4836        mode = f" {mode}" if mode else ""
4837        properties = self.sql(expression, "properties")
4838        properties = f" {properties}" if properties else ""
4839        partition = self.sql(expression, "partition")
4840        partition = f" {partition}" if partition else ""
4841        inner_expression = self.sql(expression, "expression")
4842        inner_expression = f" {inner_expression}" if inner_expression else ""
4843        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
4844
4845    def xmltable_sql(self, expression: exp.XMLTable) -> str:
4846        this = self.sql(expression, "this")
4847        namespaces = self.expressions(expression, key="namespaces")
4848        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
4849        passing = self.expressions(expression, key="passing")
4850        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
4851        columns = self.expressions(expression, key="columns")
4852        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
4853        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
4854        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
4855
4856    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
4857        this = self.sql(expression, "this")
4858        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
4859
4860    def export_sql(self, expression: exp.Export) -> str:
4861        this = self.sql(expression, "this")
4862        connection = self.sql(expression, "connection")
4863        connection = f"WITH CONNECTION {connection} " if connection else ""
4864        options = self.sql(expression, "options")
4865        return f"EXPORT DATA {connection}{options} AS {this}"
4866
4867    def declare_sql(self, expression: exp.Declare) -> str:
4868        return f"DECLARE {self.expressions(expression, flat=True)}"
4869
4870    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
4871        variable = self.sql(expression, "this")
4872        default = self.sql(expression, "default")
4873        default = f" = {default}" if default else ""
4874
4875        kind = self.sql(expression, "kind")
4876        if isinstance(expression.args.get("kind"), exp.Schema):
4877            kind = f"TABLE {kind}"
4878
4879        return f"{variable} AS {kind}{default}"
4880
4881    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
4882        kind = self.sql(expression, "kind")
4883        this = self.sql(expression, "this")
4884        set = self.sql(expression, "expression")
4885        using = self.sql(expression, "using")
4886        using = f" USING {using}" if using else ""
4887
4888        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
4889
4890        return f"{kind_sql} {this} SET {set}{using}"
4891
4892    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
4893        params = self.expressions(expression, key="params", flat=True)
4894        return self.func(expression.name, *expression.expressions) + f"({params})"
4895
4896    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
4897        return self.func(expression.name, *expression.expressions)
4898
4899    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
4900        return self.anonymousaggfunc_sql(expression)
4901
4902    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
4903        return self.parameterizedagg_sql(expression)
4904
4905    def show_sql(self, expression: exp.Show) -> str:
4906        self.unsupported("Unsupported SHOW statement")
4907        return ""
4908
4909    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
4910        # Snowflake GET/PUT statements:
4911        #   PUT <file> <internalStage> <properties>
4912        #   GET <internalStage> <file> <properties>
4913        props = expression.args.get("properties")
4914        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
4915        this = self.sql(expression, "this")
4916        target = self.sql(expression, "target")
4917
4918        if isinstance(expression, exp.Put):
4919            return f"PUT {this} {target}{props_sql}"
4920        else:
4921            return f"GET {target} {this}{props_sql}"

Generator converts a given syntax tree to the corresponding SQL string.

Arguments:
  • pretty: Whether to format the produced SQL string. Default: False.
  • identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True or 'always': Always quote. 'safe': Only quote identifiers that are case insensitive.
  • normalize: Whether to normalize identifiers to lowercase. Default: False.
  • pad: The pad size in a formatted string. For example, this affects the indentation of a projection in a query, relative to its nesting level. Default: 2.
  • indent: The indentation size in a formatted string. For example, this affects the indentation of subqueries and filters under a WHERE clause. Default: 2.
  • normalize_functions: How to normalize function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
  • unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
  • max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
  • leading_comma: Whether the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
  • max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
  • comments: Whether to preserve comments in the output SQL code. Default: True
Generator( pretty: Optional[bool] = None, identify: str | bool = False, normalize: bool = False, pad: int = 2, indent: int = 2, normalize_functions: Union[str, bool, NoneType] = None, unsupported_level: sqlglot.errors.ErrorLevel = <ErrorLevel.WARN: 'WARN'>, max_unsupported: int = 3, leading_comma: bool = False, max_text_width: int = 80, comments: bool = True, dialect: Union[str, sqlglot.dialects.Dialect, Type[sqlglot.dialects.Dialect], NoneType] = None)
687    def __init__(
688        self,
689        pretty: t.Optional[bool] = None,
690        identify: str | bool = False,
691        normalize: bool = False,
692        pad: int = 2,
693        indent: int = 2,
694        normalize_functions: t.Optional[str | bool] = None,
695        unsupported_level: ErrorLevel = ErrorLevel.WARN,
696        max_unsupported: int = 3,
697        leading_comma: bool = False,
698        max_text_width: int = 80,
699        comments: bool = True,
700        dialect: DialectType = None,
701    ):
702        import sqlglot
703        from sqlglot.dialects import Dialect
704
705        self.pretty = pretty if pretty is not None else sqlglot.pretty
706        self.identify = identify
707        self.normalize = normalize
708        self.pad = pad
709        self._indent = indent
710        self.unsupported_level = unsupported_level
711        self.max_unsupported = max_unsupported
712        self.leading_comma = leading_comma
713        self.max_text_width = max_text_width
714        self.comments = comments
715        self.dialect = Dialect.get_or_raise(dialect)
716
717        # This is both a Dialect property and a Generator argument, so we prioritize the latter
718        self.normalize_functions = (
719            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
720        )
721
722        self.unsupported_messages: t.List[str] = []
723        self._escaped_quote_end: str = (
724            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
725        )
726        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
727
728        self._next_name = name_sequence("_t")
729
730        self._identifier_start = self.dialect.IDENTIFIER_START
731        self._identifier_end = self.dialect.IDENTIFIER_END
732
733        self._quote_json_path_key_using_brackets = True
TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeColumns'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeWith'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayContainsAll'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayOverlaps'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CredentialsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EmptyProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Except'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Floor'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Get'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Intersect'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Int64'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PartitionedByBucket'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PartitionByTruncate'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Put'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Stream'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StreamingTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SwapTable'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Tags'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Union'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingTemplateProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingData'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Uuid'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithProcedureOptions'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ForceProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SET_OP_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
SUPPORTS_UESCAPE = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
QUOTE_JSON_PATH = True
PAD_FILL_PATTERN_IS_REQUIRED = False
SUPPORTS_EXPLODING_PROJECTIONS = True
ARRAY_CONCAT_IS_VAR_LEN = True
SUPPORTS_CONVERT_TIMEZONE = False
SUPPORTS_MEDIAN = True
SUPPORTS_UNIX_SECONDS = False
PARSE_JSON_NAME: Optional[str] = 'PARSE_JSON'
ARRAY_SIZE_NAME: str = 'ARRAY_LENGTH'
ALTER_SET_TYPE = 'SET DATA TYPE'
ARRAY_SIZE_DIM_REQUIRED: Optional[bool] = None
TYPE_MAPPING = {<Type.DATETIME2: 'DATETIME2'>: 'TIMESTAMP', <Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.BLOB: 'BLOB'>: 'VARBINARY', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY', <Type.SMALLDATETIME: 'SMALLDATETIME'>: 'TIMESTAMP'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: Set[str] = set()
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistributedByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DuplicateKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DynamicProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EmptyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EncodeProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.IncludeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SecurityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StorageHandlerProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StreamingTableProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StrictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Tags'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.UsingTemplateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithProcedureOptions'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ForceProperty'>: <Location.POST_CREATE: 'POST_CREATE'>}
RESERVED_KEYWORDS: Set[str] = set()
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.NVARCHAR: 'NVARCHAR'>, <Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.VARCHAR: 'VARCHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str:
735    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
736        """
737        Generates the SQL string corresponding to the given syntax tree.
738
739        Args:
740            expression: The syntax tree.
741            copy: Whether to copy the expression. The generator performs mutations so
742                it is safer to copy.
743
744        Returns:
745            The SQL string corresponding to `expression`.
746        """
747        if copy:
748            expression = expression.copy()
749
750        expression = self.preprocess(expression)
751
752        self.unsupported_messages = []
753        sql = self.sql(expression).strip()
754
755        if self.pretty:
756            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
757
758        if self.unsupported_level == ErrorLevel.IGNORE:
759            return sql
760
761        if self.unsupported_level == ErrorLevel.WARN:
762            for msg in self.unsupported_messages:
763                logger.warning(msg)
764        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
765            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
766
767        return sql

Generates the SQL string corresponding to the given syntax tree.

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

The SQL string corresponding to expression.

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

Apply generic preprocessing transformations to a given expression.

def unsupported(self, message: str) -> None:
791    def unsupported(self, message: str) -> None:
792        if self.unsupported_level == ErrorLevel.IMMEDIATE:
793            raise UnsupportedError(message)
794        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
796    def sep(self, sep: str = " ") -> str:
797        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
799    def seg(self, sql: str, sep: str = " ") -> str:
800        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
802    def pad_comment(self, comment: str) -> str:
803        comment = " " + comment if comment[0].strip() else comment
804        comment = comment + " " if comment[-1].strip() else comment
805        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None, separated: bool = False) -> str:
807    def maybe_comment(
808        self,
809        sql: str,
810        expression: t.Optional[exp.Expression] = None,
811        comments: t.Optional[t.List[str]] = None,
812        separated: bool = False,
813    ) -> str:
814        comments = (
815            ((expression and expression.comments) if comments is None else comments)  # type: ignore
816            if self.comments
817            else None
818        )
819
820        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
821            return sql
822
823        comments_sql = " ".join(
824            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
825        )
826
827        if not comments_sql:
828            return sql
829
830        comments_sql = self._replace_line_breaks(comments_sql)
831
832        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
833            return (
834                f"{self.sep()}{comments_sql}{sql}"
835                if not sql or sql[0].isspace()
836                else f"{comments_sql}{self.sep()}{sql}"
837            )
838
839        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
841    def wrap(self, expression: exp.Expression | str) -> str:
842        this_sql = (
843            self.sql(expression)
844            if isinstance(expression, exp.UNWRAPPED_QUERIES)
845            else self.sql(expression, "this")
846        )
847        if not this_sql:
848            return "()"
849
850        this_sql = self.indent(this_sql, level=1, pad=0)
851        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
853    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
854        original = self.identify
855        self.identify = False
856        result = func(*args, **kwargs)
857        self.identify = original
858        return result
def normalize_func(self, name: str) -> str:
860    def normalize_func(self, name: str) -> str:
861        if self.normalize_functions == "upper" or self.normalize_functions is True:
862            return name.upper()
863        if self.normalize_functions == "lower":
864            return name.lower()
865        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
867    def indent(
868        self,
869        sql: str,
870        level: int = 0,
871        pad: t.Optional[int] = None,
872        skip_first: bool = False,
873        skip_last: bool = False,
874    ) -> str:
875        if not self.pretty or not sql:
876            return sql
877
878        pad = self.pad if pad is None else pad
879        lines = sql.split("\n")
880
881        return "\n".join(
882            (
883                line
884                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
885                else f"{' ' * (level * self._indent + pad)}{line}"
886            )
887            for i, line in enumerate(lines)
888        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
890    def sql(
891        self,
892        expression: t.Optional[str | exp.Expression],
893        key: t.Optional[str] = None,
894        comment: bool = True,
895    ) -> str:
896        if not expression:
897            return ""
898
899        if isinstance(expression, str):
900            return expression
901
902        if key:
903            value = expression.args.get(key)
904            if value:
905                return self.sql(value)
906            return ""
907
908        transform = self.TRANSFORMS.get(expression.__class__)
909
910        if callable(transform):
911            sql = transform(self, expression)
912        elif isinstance(expression, exp.Expression):
913            exp_handler_name = f"{expression.key}_sql"
914
915            if hasattr(self, exp_handler_name):
916                sql = getattr(self, exp_handler_name)(expression)
917            elif isinstance(expression, exp.Func):
918                sql = self.function_fallback_sql(expression)
919            elif isinstance(expression, exp.Property):
920                sql = self.property_sql(expression)
921            else:
922                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
923        else:
924            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
925
926        return self.maybe_comment(sql, expression) if self.comments and comment else sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
928    def uncache_sql(self, expression: exp.Uncache) -> str:
929        table = self.sql(expression, "this")
930        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
931        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
933    def cache_sql(self, expression: exp.Cache) -> str:
934        lazy = " LAZY" if expression.args.get("lazy") else ""
935        table = self.sql(expression, "this")
936        options = expression.args.get("options")
937        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
938        sql = self.sql(expression, "expression")
939        sql = f" AS{self.sep()}{sql}" if sql else ""
940        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
941        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
943    def characterset_sql(self, expression: exp.CharacterSet) -> str:
944        if isinstance(expression.parent, exp.Cast):
945            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
946        default = "DEFAULT " if expression.args.get("default") else ""
947        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_parts(self, expression: sqlglot.expressions.Column) -> str:
949    def column_parts(self, expression: exp.Column) -> str:
950        return ".".join(
951            self.sql(part)
952            for part in (
953                expression.args.get("catalog"),
954                expression.args.get("db"),
955                expression.args.get("table"),
956                expression.args.get("this"),
957            )
958            if part
959        )
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
961    def column_sql(self, expression: exp.Column) -> str:
962        join_mark = " (+)" if expression.args.get("join_mark") else ""
963
964        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
965            join_mark = ""
966            self.unsupported("Outer join syntax using the (+) operator is not supported.")
967
968        return f"{self.column_parts(expression)}{join_mark}"
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
970    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
971        this = self.sql(expression, "this")
972        this = f" {this}" if this else ""
973        position = self.sql(expression, "position")
974        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
976    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
977        column = self.sql(expression, "this")
978        kind = self.sql(expression, "kind")
979        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
980        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
981        kind = f"{sep}{kind}" if kind else ""
982        constraints = f" {constraints}" if constraints else ""
983        position = self.sql(expression, "position")
984        position = f" {position}" if position else ""
985
986        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
987            kind = ""
988
989        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
991    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
992        this = self.sql(expression, "this")
993        kind_sql = self.sql(expression, "kind").strip()
994        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
 996    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
 997        this = self.sql(expression, "this")
 998        if expression.args.get("not_null"):
 999            persisted = " PERSISTED NOT NULL"
1000        elif expression.args.get("persisted"):
1001            persisted = " PERSISTED"
1002        else:
1003            persisted = ""
1004        return f"AS {this}{persisted}"
def autoincrementcolumnconstraint_sql(self, _) -> str:
1006    def autoincrementcolumnconstraint_sql(self, _) -> str:
1007        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
1009    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
1010        if isinstance(expression.this, list):
1011            this = self.wrap(self.expressions(expression, key="this", flat=True))
1012        else:
1013            this = self.sql(expression, "this")
1014
1015        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
1017    def generatedasidentitycolumnconstraint_sql(
1018        self, expression: exp.GeneratedAsIdentityColumnConstraint
1019    ) -> str:
1020        this = ""
1021        if expression.this is not None:
1022            on_null = " ON NULL" if expression.args.get("on_null") else ""
1023            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
1024
1025        start = expression.args.get("start")
1026        start = f"START WITH {start}" if start else ""
1027        increment = expression.args.get("increment")
1028        increment = f" INCREMENT BY {increment}" if increment else ""
1029        minvalue = expression.args.get("minvalue")
1030        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1031        maxvalue = expression.args.get("maxvalue")
1032        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1033        cycle = expression.args.get("cycle")
1034        cycle_sql = ""
1035
1036        if cycle is not None:
1037            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
1038            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
1039
1040        sequence_opts = ""
1041        if start or increment or cycle_sql:
1042            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
1043            sequence_opts = f" ({sequence_opts.strip()})"
1044
1045        expr = self.sql(expression, "expression")
1046        expr = f"({expr})" if expr else "IDENTITY"
1047
1048        return f"GENERATED{this} AS {expr}{sequence_opts}"
def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str:
1050    def generatedasrowcolumnconstraint_sql(
1051        self, expression: exp.GeneratedAsRowColumnConstraint
1052    ) -> str:
1053        start = "START" if expression.args.get("start") else "END"
1054        hidden = " HIDDEN" if expression.args.get("hidden") else ""
1055        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str:
1057    def periodforsystemtimeconstraint_sql(
1058        self, expression: exp.PeriodForSystemTimeConstraint
1059    ) -> str:
1060        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
1062    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
1063        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str:
1065    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
1066        return f"AS {self.sql(expression, 'this')}"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
1068    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
1069        desc = expression.args.get("desc")
1070        if desc is not None:
1071            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
1072        options = self.expressions(expression, key="options", flat=True, sep=" ")
1073        options = f" {options}" if options else ""
1074        return f"PRIMARY KEY{options}"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
1076    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
1077        this = self.sql(expression, "this")
1078        this = f" {this}" if this else ""
1079        index_type = expression.args.get("index_type")
1080        index_type = f" USING {index_type}" if index_type else ""
1081        on_conflict = self.sql(expression, "on_conflict")
1082        on_conflict = f" {on_conflict}" if on_conflict else ""
1083        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
1084        options = self.expressions(expression, key="options", flat=True, sep=" ")
1085        options = f" {options}" if options else ""
1086        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}{options}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str:
1088    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
1089        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
1091    def create_sql(self, expression: exp.Create) -> str:
1092        kind = self.sql(expression, "kind")
1093        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1094        properties = expression.args.get("properties")
1095        properties_locs = self.locate_properties(properties) if properties else defaultdict()
1096
1097        this = self.createable_sql(expression, properties_locs)
1098
1099        properties_sql = ""
1100        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
1101            exp.Properties.Location.POST_WITH
1102        ):
1103            properties_sql = self.sql(
1104                exp.Properties(
1105                    expressions=[
1106                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
1107                        *properties_locs[exp.Properties.Location.POST_WITH],
1108                    ]
1109                )
1110            )
1111
1112            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
1113                properties_sql = self.sep() + properties_sql
1114            elif not self.pretty:
1115                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
1116                properties_sql = f" {properties_sql}"
1117
1118        begin = " BEGIN" if expression.args.get("begin") else ""
1119        end = " END" if expression.args.get("end") else ""
1120
1121        expression_sql = self.sql(expression, "expression")
1122        if expression_sql:
1123            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
1124
1125            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
1126                postalias_props_sql = ""
1127                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
1128                    postalias_props_sql = self.properties(
1129                        exp.Properties(
1130                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
1131                        ),
1132                        wrapped=False,
1133                    )
1134                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
1135                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
1136
1137        postindex_props_sql = ""
1138        if properties_locs.get(exp.Properties.Location.POST_INDEX):
1139            postindex_props_sql = self.properties(
1140                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
1141                wrapped=False,
1142                prefix=" ",
1143            )
1144
1145        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
1146        indexes = f" {indexes}" if indexes else ""
1147        index_sql = indexes + postindex_props_sql
1148
1149        replace = " OR REPLACE" if expression.args.get("replace") else ""
1150        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
1151        unique = " UNIQUE" if expression.args.get("unique") else ""
1152
1153        clustered = expression.args.get("clustered")
1154        if clustered is None:
1155            clustered_sql = ""
1156        elif clustered:
1157            clustered_sql = " CLUSTERED COLUMNSTORE"
1158        else:
1159            clustered_sql = " NONCLUSTERED COLUMNSTORE"
1160
1161        postcreate_props_sql = ""
1162        if properties_locs.get(exp.Properties.Location.POST_CREATE):
1163            postcreate_props_sql = self.properties(
1164                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
1165                sep=" ",
1166                prefix=" ",
1167                wrapped=False,
1168            )
1169
1170        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
1171
1172        postexpression_props_sql = ""
1173        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
1174            postexpression_props_sql = self.properties(
1175                exp.Properties(
1176                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
1177                ),
1178                sep=" ",
1179                prefix=" ",
1180                wrapped=False,
1181            )
1182
1183        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1184        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
1185        no_schema_binding = (
1186            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
1187        )
1188
1189        clone = self.sql(expression, "clone")
1190        clone = f" {clone}" if clone else ""
1191
1192        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
1193            properties_expression = f"{expression_sql}{properties_sql}"
1194        else:
1195            properties_expression = f"{properties_sql}{expression_sql}"
1196
1197        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
1198        return self.prepend_ctes(expression, expression_sql)
def sequenceproperties_sql(self, expression: sqlglot.expressions.SequenceProperties) -> str:
1200    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
1201        start = self.sql(expression, "start")
1202        start = f"START WITH {start}" if start else ""
1203        increment = self.sql(expression, "increment")
1204        increment = f" INCREMENT BY {increment}" if increment else ""
1205        minvalue = self.sql(expression, "minvalue")
1206        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
1207        maxvalue = self.sql(expression, "maxvalue")
1208        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
1209        owned = self.sql(expression, "owned")
1210        owned = f" OWNED BY {owned}" if owned else ""
1211
1212        cache = expression.args.get("cache")
1213        if cache is None:
1214            cache_str = ""
1215        elif cache is True:
1216            cache_str = " CACHE"
1217        else:
1218            cache_str = f" CACHE {cache}"
1219
1220        options = self.expressions(expression, key="options", flat=True, sep=" ")
1221        options = f" {options}" if options else ""
1222
1223        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
1225    def clone_sql(self, expression: exp.Clone) -> str:
1226        this = self.sql(expression, "this")
1227        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1228        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1229        return f"{shallow}{keyword} {this}"
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
1231    def describe_sql(self, expression: exp.Describe) -> str:
1232        style = expression.args.get("style")
1233        style = f" {style}" if style else ""
1234        partition = self.sql(expression, "partition")
1235        partition = f" {partition}" if partition else ""
1236        format = self.sql(expression, "format")
1237        format = f" {format}" if format else ""
1238
1239        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str:
1241    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1242        tag = self.sql(expression, "tag")
1243        return f"${tag}${self.sql(expression, 'this')}${tag}$"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
1245    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1246        with_ = self.sql(expression, "with")
1247        if with_:
1248            sql = f"{with_}{self.sep()}{sql}"
1249        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
1251    def with_sql(self, expression: exp.With) -> str:
1252        sql = self.expressions(expression, flat=True)
1253        recursive = (
1254            "RECURSIVE "
1255            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1256            else ""
1257        )
1258        search = self.sql(expression, "search")
1259        search = f" {search}" if search else ""
1260
1261        return f"WITH {recursive}{sql}{search}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
1263    def cte_sql(self, expression: exp.CTE) -> str:
1264        alias = expression.args.get("alias")
1265        if alias:
1266            alias.add_comments(expression.pop_comments())
1267
1268        alias_sql = self.sql(expression, "alias")
1269
1270        materialized = expression.args.get("materialized")
1271        if materialized is False:
1272            materialized = "NOT MATERIALIZED "
1273        elif materialized:
1274            materialized = "MATERIALIZED "
1275
1276        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
1278    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1279        alias = self.sql(expression, "this")
1280        columns = self.expressions(expression, key="columns", flat=True)
1281        columns = f"({columns})" if columns else ""
1282
1283        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1284            columns = ""
1285            self.unsupported("Named columns are not supported in table alias.")
1286
1287        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1288            alias = self._next_name()
1289
1290        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
1292    def bitstring_sql(self, expression: exp.BitString) -> str:
1293        this = self.sql(expression, "this")
1294        if self.dialect.BIT_START:
1295            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1296        return f"{int(this, 2)}"
def hexstring_sql( self, expression: sqlglot.expressions.HexString, binary_function_repr: Optional[str] = None) -> str:
1298    def hexstring_sql(
1299        self, expression: exp.HexString, binary_function_repr: t.Optional[str] = None
1300    ) -> str:
1301        this = self.sql(expression, "this")
1302        is_integer_type = expression.args.get("is_integer")
1303
1304        if (is_integer_type and not self.dialect.HEX_STRING_IS_INTEGER_TYPE) or (
1305            not self.dialect.HEX_START and not binary_function_repr
1306        ):
1307            # Integer representation will be returned if:
1308            # - The read dialect treats the hex value as integer literal but not the write
1309            # - The transpilation is not supported (write dialect hasn't set HEX_START or the param flag)
1310            return f"{int(this, 16)}"
1311
1312        if not is_integer_type:
1313            # Read dialect treats the hex value as BINARY/BLOB
1314            if binary_function_repr:
1315                # The write dialect supports the transpilation to its equivalent BINARY/BLOB
1316                return self.func(binary_function_repr, exp.Literal.string(this))
1317            if self.dialect.HEX_STRING_IS_INTEGER_TYPE:
1318                # The write dialect does not support the transpilation, it'll treat the hex value as INTEGER
1319                self.unsupported("Unsupported transpilation from BINARY/BLOB hex string")
1320
1321        return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
1323    def bytestring_sql(self, expression: exp.ByteString) -> str:
1324        this = self.sql(expression, "this")
1325        if self.dialect.BYTE_START:
1326            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1327        return this
def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str:
1329    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1330        this = self.sql(expression, "this")
1331        escape = expression.args.get("escape")
1332
1333        if self.dialect.UNICODE_START:
1334            escape_substitute = r"\\\1"
1335            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
1336        else:
1337            escape_substitute = r"\\u\1"
1338            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
1339
1340        if escape:
1341            escape_pattern = re.compile(rf"{escape.name}(\d+)")
1342            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
1343        else:
1344            escape_pattern = ESCAPED_UNICODE_RE
1345            escape_sql = ""
1346
1347        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
1348            this = escape_pattern.sub(escape_substitute, this)
1349
1350        return f"{left_quote}{this}{right_quote}{escape_sql}"
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
1352    def rawstring_sql(self, expression: exp.RawString) -> str:
1353        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
1354        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str:
1356    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1357        this = self.sql(expression, "this")
1358        specifier = self.sql(expression, "expression")
1359        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1360        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
1362    def datatype_sql(self, expression: exp.DataType) -> str:
1363        nested = ""
1364        values = ""
1365        interior = self.expressions(expression, flat=True)
1366
1367        type_value = expression.this
1368        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1369            type_sql = self.sql(expression, "kind")
1370        else:
1371            type_sql = (
1372                self.TYPE_MAPPING.get(type_value, type_value.value)
1373                if isinstance(type_value, exp.DataType.Type)
1374                else type_value
1375            )
1376
1377        if interior:
1378            if expression.args.get("nested"):
1379                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1380                if expression.args.get("values") is not None:
1381                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1382                    values = self.expressions(expression, key="values", flat=True)
1383                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1384            elif type_value == exp.DataType.Type.INTERVAL:
1385                nested = f" {interior}"
1386            else:
1387                nested = f"({interior})"
1388
1389        type_sql = f"{type_sql}{nested}{values}"
1390        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1391            exp.DataType.Type.TIMETZ,
1392            exp.DataType.Type.TIMESTAMPTZ,
1393        ):
1394            type_sql = f"{type_sql} WITH TIME ZONE"
1395
1396        return type_sql
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
1398    def directory_sql(self, expression: exp.Directory) -> str:
1399        local = "LOCAL " if expression.args.get("local") else ""
1400        row_format = self.sql(expression, "row_format")
1401        row_format = f" {row_format}" if row_format else ""
1402        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
1404    def delete_sql(self, expression: exp.Delete) -> str:
1405        this = self.sql(expression, "this")
1406        this = f" FROM {this}" if this else ""
1407        using = self.sql(expression, "using")
1408        using = f" USING {using}" if using else ""
1409        cluster = self.sql(expression, "cluster")
1410        cluster = f" {cluster}" if cluster else ""
1411        where = self.sql(expression, "where")
1412        returning = self.sql(expression, "returning")
1413        limit = self.sql(expression, "limit")
1414        tables = self.expressions(expression, key="tables")
1415        tables = f" {tables}" if tables else ""
1416        if self.RETURNING_END:
1417            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
1418        else:
1419            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
1420        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
1422    def drop_sql(self, expression: exp.Drop) -> str:
1423        this = self.sql(expression, "this")
1424        expressions = self.expressions(expression, flat=True)
1425        expressions = f" ({expressions})" if expressions else ""
1426        kind = expression.args["kind"]
1427        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
1428        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1429        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
1430        on_cluster = self.sql(expression, "cluster")
1431        on_cluster = f" {on_cluster}" if on_cluster else ""
1432        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1433        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1434        cascade = " CASCADE" if expression.args.get("cascade") else ""
1435        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1436        purge = " PURGE" if expression.args.get("purge") else ""
1437        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
def set_operation(self, expression: sqlglot.expressions.SetOperation) -> str:
1439    def set_operation(self, expression: exp.SetOperation) -> str:
1440        op_type = type(expression)
1441        op_name = op_type.key.upper()
1442
1443        distinct = expression.args.get("distinct")
1444        if (
1445            distinct is False
1446            and op_type in (exp.Except, exp.Intersect)
1447            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
1448        ):
1449            self.unsupported(f"{op_name} ALL is not supported")
1450
1451        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
1452
1453        if distinct is None:
1454            distinct = default_distinct
1455            if distinct is None:
1456                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
1457
1458        if distinct is default_distinct:
1459            distinct_or_all = ""
1460        else:
1461            distinct_or_all = " DISTINCT" if distinct else " ALL"
1462
1463        side_kind = " ".join(filter(None, [expression.side, expression.kind]))
1464        side_kind = f"{side_kind} " if side_kind else ""
1465
1466        by_name = " BY NAME" if expression.args.get("by_name") else ""
1467        on = self.expressions(expression, key="on", flat=True)
1468        on = f" ON ({on})" if on else ""
1469
1470        return f"{side_kind}{op_name}{distinct_or_all}{by_name}{on}"
def set_operations(self, expression: sqlglot.expressions.SetOperation) -> str:
1472    def set_operations(self, expression: exp.SetOperation) -> str:
1473        if not self.SET_OP_MODIFIERS:
1474            limit = expression.args.get("limit")
1475            order = expression.args.get("order")
1476
1477            if limit or order:
1478                select = self._move_ctes_to_top_level(
1479                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
1480                )
1481
1482                if limit:
1483                    select = select.limit(limit.pop(), copy=False)
1484                if order:
1485                    select = select.order_by(order.pop(), copy=False)
1486                return self.sql(select)
1487
1488        sqls: t.List[str] = []
1489        stack: t.List[t.Union[str, exp.Expression]] = [expression]
1490
1491        while stack:
1492            node = stack.pop()
1493
1494            if isinstance(node, exp.SetOperation):
1495                stack.append(node.expression)
1496                stack.append(
1497                    self.maybe_comment(
1498                        self.set_operation(node), comments=node.comments, separated=True
1499                    )
1500                )
1501                stack.append(node.this)
1502            else:
1503                sqls.append(self.sql(node))
1504
1505        this = self.sep().join(sqls)
1506        this = self.query_modifiers(expression, this)
1507        return self.prepend_ctes(expression, this)
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
1509    def fetch_sql(self, expression: exp.Fetch) -> str:
1510        direction = expression.args.get("direction")
1511        direction = f" {direction}" if direction else ""
1512        count = self.sql(expression, "count")
1513        count = f" {count}" if count else ""
1514        limit_options = self.sql(expression, "limit_options")
1515        limit_options = f"{limit_options}" if limit_options else " ROWS ONLY"
1516        return f"{self.seg('FETCH')}{direction}{count}{limit_options}"
def limitoptions_sql(self, expression: sqlglot.expressions.LimitOptions) -> str:
1518    def limitoptions_sql(self, expression: exp.LimitOptions) -> str:
1519        percent = " PERCENT" if expression.args.get("percent") else ""
1520        rows = " ROWS" if expression.args.get("rows") else ""
1521        with_ties = " WITH TIES" if expression.args.get("with_ties") else ""
1522        if not with_ties and rows:
1523            with_ties = " ONLY"
1524        return f"{percent}{rows}{with_ties}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
1526    def filter_sql(self, expression: exp.Filter) -> str:
1527        if self.AGGREGATE_FILTER_SUPPORTED:
1528            this = self.sql(expression, "this")
1529            where = self.sql(expression, "expression").strip()
1530            return f"{this} FILTER({where})"
1531
1532        agg = expression.this
1533        agg_arg = agg.this
1534        cond = expression.expression.this
1535        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1536        return self.sql(agg)
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
1538    def hint_sql(self, expression: exp.Hint) -> str:
1539        if not self.QUERY_HINTS:
1540            self.unsupported("Hints are not supported")
1541            return ""
1542
1543        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
def indexparameters_sql(self, expression: sqlglot.expressions.IndexParameters) -> str:
1545    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
1546        using = self.sql(expression, "using")
1547        using = f" USING {using}" if using else ""
1548        columns = self.expressions(expression, key="columns", flat=True)
1549        columns = f"({columns})" if columns else ""
1550        partition_by = self.expressions(expression, key="partition_by", flat=True)
1551        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1552        where = self.sql(expression, "where")
1553        include = self.expressions(expression, key="include", flat=True)
1554        if include:
1555            include = f" INCLUDE ({include})"
1556        with_storage = self.expressions(expression, key="with_storage", flat=True)
1557        with_storage = f" WITH ({with_storage})" if with_storage else ""
1558        tablespace = self.sql(expression, "tablespace")
1559        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
1560        on = self.sql(expression, "on")
1561        on = f" ON {on}" if on else ""
1562
1563        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
1565    def index_sql(self, expression: exp.Index) -> str:
1566        unique = "UNIQUE " if expression.args.get("unique") else ""
1567        primary = "PRIMARY " if expression.args.get("primary") else ""
1568        amp = "AMP " if expression.args.get("amp") else ""
1569        name = self.sql(expression, "this")
1570        name = f"{name} " if name else ""
1571        table = self.sql(expression, "table")
1572        table = f"{self.INDEX_ON} {table}" if table else ""
1573
1574        index = "INDEX " if not table else ""
1575
1576        params = self.sql(expression, "params")
1577        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
1579    def identifier_sql(self, expression: exp.Identifier) -> str:
1580        text = expression.name
1581        lower = text.lower()
1582        text = lower if self.normalize and not expression.quoted else text
1583        text = text.replace(self._identifier_end, self._escaped_identifier_end)
1584        if (
1585            expression.quoted
1586            or self.dialect.can_identify(text, self.identify)
1587            or lower in self.RESERVED_KEYWORDS
1588            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1589        ):
1590            text = f"{self._identifier_start}{text}{self._identifier_end}"
1591        return text
def hex_sql(self, expression: sqlglot.expressions.Hex) -> str:
1593    def hex_sql(self, expression: exp.Hex) -> str:
1594        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1595        if self.dialect.HEX_LOWERCASE:
1596            text = self.func("LOWER", text)
1597
1598        return text
def lowerhex_sql(self, expression: sqlglot.expressions.LowerHex) -> str:
1600    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
1601        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
1602        if not self.dialect.HEX_LOWERCASE:
1603            text = self.func("LOWER", text)
1604        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
1606    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1607        input_format = self.sql(expression, "input_format")
1608        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1609        output_format = self.sql(expression, "output_format")
1610        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1611        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
1613    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1614        string = self.sql(exp.Literal.string(expression.name))
1615        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
1617    def partition_sql(self, expression: exp.Partition) -> str:
1618        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
1619        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
1621    def properties_sql(self, expression: exp.Properties) -> str:
1622        root_properties = []
1623        with_properties = []
1624
1625        for p in expression.expressions:
1626            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1627            if p_loc == exp.Properties.Location.POST_WITH:
1628                with_properties.append(p)
1629            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1630                root_properties.append(p)
1631
1632        root_props = self.root_properties(exp.Properties(expressions=root_properties))
1633        with_props = self.with_properties(exp.Properties(expressions=with_properties))
1634
1635        if root_props and with_props and not self.pretty:
1636            with_props = " " + with_props
1637
1638        return root_props + with_props
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
1640    def root_properties(self, properties: exp.Properties) -> str:
1641        if properties.expressions:
1642            return self.expressions(properties, indent=False, sep=" ")
1643        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1645    def properties(
1646        self,
1647        properties: exp.Properties,
1648        prefix: str = "",
1649        sep: str = ", ",
1650        suffix: str = "",
1651        wrapped: bool = True,
1652    ) -> str:
1653        if properties.expressions:
1654            expressions = self.expressions(properties, sep=sep, indent=False)
1655            if expressions:
1656                expressions = self.wrap(expressions) if wrapped else expressions
1657                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1658        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
1660    def with_properties(self, properties: exp.Properties) -> str:
1661        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict:
1663    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1664        properties_locs = defaultdict(list)
1665        for p in properties.expressions:
1666            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1667            if p_loc != exp.Properties.Location.UNSUPPORTED:
1668                properties_locs[p_loc].append(p)
1669            else:
1670                self.unsupported(f"Unsupported property {p.key}")
1671
1672        return properties_locs
def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1674    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1675        if isinstance(expression.this, exp.Dot):
1676            return self.sql(expression, "this")
1677        return f"'{expression.name}'" if string_key else expression.name
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
1679    def property_sql(self, expression: exp.Property) -> str:
1680        property_cls = expression.__class__
1681        if property_cls == exp.Property:
1682            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1683
1684        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1685        if not property_name:
1686            self.unsupported(f"Unsupported property {expression.key}")
1687
1688        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
1690    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1691        if self.SUPPORTS_CREATE_TABLE_LIKE:
1692            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1693            options = f" {options}" if options else ""
1694
1695            like = f"LIKE {self.sql(expression, 'this')}{options}"
1696            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1697                like = f"({like})"
1698
1699            return like
1700
1701        if expression.expressions:
1702            self.unsupported("Transpilation of LIKE property options is unsupported")
1703
1704        select = exp.select("*").from_(expression.this).limit(0)
1705        return f"AS {self.sql(select)}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
1707    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1708        no = "NO " if expression.args.get("no") else ""
1709        protection = " PROTECTION" if expression.args.get("protection") else ""
1710        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
1712    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1713        no = "NO " if expression.args.get("no") else ""
1714        local = expression.args.get("local")
1715        local = f"{local} " if local else ""
1716        dual = "DUAL " if expression.args.get("dual") else ""
1717        before = "BEFORE " if expression.args.get("before") else ""
1718        after = "AFTER " if expression.args.get("after") else ""
1719        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1721    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1722        freespace = self.sql(expression, "this")
1723        percent = " PERCENT" if expression.args.get("percent") else ""
1724        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1726    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1727        if expression.args.get("default"):
1728            property = "DEFAULT"
1729        elif expression.args.get("on"):
1730            property = "ON"
1731        else:
1732            property = "OFF"
1733        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1735    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1736        if expression.args.get("no"):
1737            return "NO MERGEBLOCKRATIO"
1738        if expression.args.get("default"):
1739            return "DEFAULT MERGEBLOCKRATIO"
1740
1741        percent = " PERCENT" if expression.args.get("percent") else ""
1742        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1744    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1745        default = expression.args.get("default")
1746        minimum = expression.args.get("minimum")
1747        maximum = expression.args.get("maximum")
1748        if default or minimum or maximum:
1749            if default:
1750                prop = "DEFAULT"
1751            elif minimum:
1752                prop = "MINIMUM"
1753            else:
1754                prop = "MAXIMUM"
1755            return f"{prop} DATABLOCKSIZE"
1756        units = expression.args.get("units")
1757        units = f" {units}" if units else ""
1758        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1760    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1761        autotemp = expression.args.get("autotemp")
1762        always = expression.args.get("always")
1763        default = expression.args.get("default")
1764        manual = expression.args.get("manual")
1765        never = expression.args.get("never")
1766
1767        if autotemp is not None:
1768            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1769        elif always:
1770            prop = "ALWAYS"
1771        elif default:
1772            prop = "DEFAULT"
1773        elif manual:
1774            prop = "MANUAL"
1775        elif never:
1776            prop = "NEVER"
1777        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1779    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1780        no = expression.args.get("no")
1781        no = " NO" if no else ""
1782        concurrent = expression.args.get("concurrent")
1783        concurrent = " CONCURRENT" if concurrent else ""
1784        target = self.sql(expression, "target")
1785        target = f" {target}" if target else ""
1786        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str:
1788    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1789        if isinstance(expression.this, list):
1790            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1791        if expression.this:
1792            modulus = self.sql(expression, "this")
1793            remainder = self.sql(expression, "expression")
1794            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1795
1796        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1797        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1798        return f"FROM ({from_expressions}) TO ({to_expressions})"
def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str:
1800    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1801        this = self.sql(expression, "this")
1802
1803        for_values_or_default = expression.expression
1804        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1805            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1806        else:
1807            for_values_or_default = " DEFAULT"
1808
1809        return f"PARTITION OF {this}{for_values_or_default}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1811    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1812        kind = expression.args.get("kind")
1813        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1814        for_or_in = expression.args.get("for_or_in")
1815        for_or_in = f" {for_or_in}" if for_or_in else ""
1816        lock_type = expression.args.get("lock_type")
1817        override = " OVERRIDE" if expression.args.get("override") else ""
1818        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1820    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1821        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1822        statistics = expression.args.get("statistics")
1823        statistics_sql = ""
1824        if statistics is not None:
1825            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1826        return f"{data_sql}{statistics_sql}"
def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1828    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1829        this = self.sql(expression, "this")
1830        this = f"HISTORY_TABLE={this}" if this else ""
1831        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
1832        data_consistency = (
1833            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
1834        )
1835        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
1836        retention_period = (
1837            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
1838        )
1839
1840        if this:
1841            on_sql = self.func("ON", this, data_consistency, retention_period)
1842        else:
1843            on_sql = "ON" if expression.args.get("on") else "OFF"
1844
1845        sql = f"SYSTEM_VERSIONING={on_sql}"
1846
1847        return f"WITH({sql})" if expression.args.get("with") else sql
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1849    def insert_sql(self, expression: exp.Insert) -> str:
1850        hint = self.sql(expression, "hint")
1851        overwrite = expression.args.get("overwrite")
1852
1853        if isinstance(expression.this, exp.Directory):
1854            this = " OVERWRITE" if overwrite else " INTO"
1855        else:
1856            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1857
1858        stored = self.sql(expression, "stored")
1859        stored = f" {stored}" if stored else ""
1860        alternative = expression.args.get("alternative")
1861        alternative = f" OR {alternative}" if alternative else ""
1862        ignore = " IGNORE" if expression.args.get("ignore") else ""
1863        is_function = expression.args.get("is_function")
1864        if is_function:
1865            this = f"{this} FUNCTION"
1866        this = f"{this} {self.sql(expression, 'this')}"
1867
1868        exists = " IF EXISTS" if expression.args.get("exists") else ""
1869        where = self.sql(expression, "where")
1870        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1871        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1872        on_conflict = self.sql(expression, "conflict")
1873        on_conflict = f" {on_conflict}" if on_conflict else ""
1874        by_name = " BY NAME" if expression.args.get("by_name") else ""
1875        returning = self.sql(expression, "returning")
1876
1877        if self.RETURNING_END:
1878            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1879        else:
1880            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1881
1882        partition_by = self.sql(expression, "partition")
1883        partition_by = f" {partition_by}" if partition_by else ""
1884        settings = self.sql(expression, "settings")
1885        settings = f" {settings}" if settings else ""
1886
1887        source = self.sql(expression, "source")
1888        source = f"TABLE {source}" if source else ""
1889
1890        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
1891        return self.prepend_ctes(expression, sql)
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1893    def introducer_sql(self, expression: exp.Introducer) -> str:
1894        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def kill_sql(self, expression: sqlglot.expressions.Kill) -> str:
1896    def kill_sql(self, expression: exp.Kill) -> str:
1897        kind = self.sql(expression, "kind")
1898        kind = f" {kind}" if kind else ""
1899        this = self.sql(expression, "this")
1900        this = f" {this}" if this else ""
1901        return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1903    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1904        return expression.name
def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str:
1906    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1907        return expression.name
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1909    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1910        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1911
1912        constraint = self.sql(expression, "constraint")
1913        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1914
1915        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1916        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1917        action = self.sql(expression, "action")
1918
1919        expressions = self.expressions(expression, flat=True)
1920        if expressions:
1921            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1922            expressions = f" {set_keyword}{expressions}"
1923
1924        where = self.sql(expression, "where")
1925        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1927    def returning_sql(self, expression: exp.Returning) -> str:
1928        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1930    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1931        fields = self.sql(expression, "fields")
1932        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1933        escaped = self.sql(expression, "escaped")
1934        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1935        items = self.sql(expression, "collection_items")
1936        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1937        keys = self.sql(expression, "map_keys")
1938        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1939        lines = self.sql(expression, "lines")
1940        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1941        null = self.sql(expression, "null")
1942        null = f" NULL DEFINED AS {null}" if null else ""
1943        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str:
1945    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1946        return f"WITH ({self.expressions(expression, flat=True)})"
def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str:
1948    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1949        this = f"{self.sql(expression, 'this')} INDEX"
1950        target = self.sql(expression, "target")
1951        target = f" FOR {target}" if target else ""
1952        return f"{this}{target} ({self.expressions(expression, flat=True)})"
def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str:
1954    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1955        this = self.sql(expression, "this")
1956        kind = self.sql(expression, "kind")
1957        expr = self.sql(expression, "expression")
1958        return f"{this} ({kind} => {expr})"
def table_parts(self, expression: sqlglot.expressions.Table) -> str:
1960    def table_parts(self, expression: exp.Table) -> str:
1961        return ".".join(
1962            self.sql(part)
1963            for part in (
1964                expression.args.get("catalog"),
1965                expression.args.get("db"),
1966                expression.args.get("this"),
1967            )
1968            if part is not None
1969        )
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
1971    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1972        table = self.table_parts(expression)
1973        only = "ONLY " if expression.args.get("only") else ""
1974        partition = self.sql(expression, "partition")
1975        partition = f" {partition}" if partition else ""
1976        version = self.sql(expression, "version")
1977        version = f" {version}" if version else ""
1978        alias = self.sql(expression, "alias")
1979        alias = f"{sep}{alias}" if alias else ""
1980
1981        sample = self.sql(expression, "sample")
1982        if self.dialect.ALIAS_POST_TABLESAMPLE:
1983            sample_pre_alias = sample
1984            sample_post_alias = ""
1985        else:
1986            sample_pre_alias = ""
1987            sample_post_alias = sample
1988
1989        hints = self.expressions(expression, key="hints", sep=" ")
1990        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1991        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
1992        joins = self.indent(
1993            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
1994        )
1995        laterals = self.expressions(expression, key="laterals", sep="")
1996
1997        file_format = self.sql(expression, "format")
1998        if file_format:
1999            pattern = self.sql(expression, "pattern")
2000            pattern = f", PATTERN => {pattern}" if pattern else ""
2001            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
2002
2003        ordinality = expression.args.get("ordinality") or ""
2004        if ordinality:
2005            ordinality = f" WITH ORDINALITY{alias}"
2006            alias = ""
2007
2008        when = self.sql(expression, "when")
2009        if when:
2010            table = f"{table} {when}"
2011
2012        changes = self.sql(expression, "changes")
2013        changes = f" {changes}" if changes else ""
2014
2015        rows_from = self.expressions(expression, key="rows_from")
2016        if rows_from:
2017            table = f"ROWS FROM {self.wrap(rows_from)}"
2018
2019        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
def tablefromrows_sql(self, expression: sqlglot.expressions.TableFromRows) -> str:
2021    def tablefromrows_sql(self, expression: exp.TableFromRows) -> str:
2022        table = self.func("TABLE", expression.this)
2023        alias = self.sql(expression, "alias")
2024        alias = f" AS {alias}" if alias else ""
2025        sample = self.sql(expression, "sample")
2026        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2027        joins = self.indent(
2028            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
2029        )
2030        return f"{table}{alias}{pivots}{sample}{joins}"
def tablesample_sql( self, expression: sqlglot.expressions.TableSample, tablesample_keyword: Optional[str] = None) -> str:
2032    def tablesample_sql(
2033        self,
2034        expression: exp.TableSample,
2035        tablesample_keyword: t.Optional[str] = None,
2036    ) -> str:
2037        method = self.sql(expression, "method")
2038        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
2039        numerator = self.sql(expression, "bucket_numerator")
2040        denominator = self.sql(expression, "bucket_denominator")
2041        field = self.sql(expression, "bucket_field")
2042        field = f" ON {field}" if field else ""
2043        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
2044        seed = self.sql(expression, "seed")
2045        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
2046
2047        size = self.sql(expression, "size")
2048        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
2049            size = f"{size} ROWS"
2050
2051        percent = self.sql(expression, "percent")
2052        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
2053            percent = f"{percent} PERCENT"
2054
2055        expr = f"{bucket}{percent}{size}"
2056        if self.TABLESAMPLE_REQUIRES_PARENS:
2057            expr = f"({expr})"
2058
2059        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
2061    def pivot_sql(self, expression: exp.Pivot) -> str:
2062        expressions = self.expressions(expression, flat=True)
2063        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
2064
2065        group = self.sql(expression, "group")
2066
2067        if expression.this:
2068            this = self.sql(expression, "this")
2069            if not expressions:
2070                return f"UNPIVOT {this}"
2071
2072            on = f"{self.seg('ON')} {expressions}"
2073            into = self.sql(expression, "into")
2074            into = f"{self.seg('INTO')} {into}" if into else ""
2075            using = self.expressions(expression, key="using", flat=True)
2076            using = f"{self.seg('USING')} {using}" if using else ""
2077            return f"{direction} {this}{on}{into}{using}{group}"
2078
2079        alias = self.sql(expression, "alias")
2080        alias = f" AS {alias}" if alias else ""
2081
2082        fields = self.expressions(
2083            expression,
2084            "fields",
2085            sep=" ",
2086            dynamic=True,
2087            new_line=True,
2088            skip_first=True,
2089            skip_last=True,
2090        )
2091
2092        include_nulls = expression.args.get("include_nulls")
2093        if include_nulls is not None:
2094            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
2095        else:
2096            nulls = ""
2097
2098        default_on_null = self.sql(expression, "default_on_null")
2099        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
2100        return f"{self.seg(direction)}{nulls}({expressions} FOR {fields}{default_on_null}{group}){alias}"
def version_sql(self, expression: sqlglot.expressions.Version) -> str:
2102    def version_sql(self, expression: exp.Version) -> str:
2103        this = f"FOR {expression.name}"
2104        kind = expression.text("kind")
2105        expr = self.sql(expression, "expression")
2106        return f"{this} {kind} {expr}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
2108    def tuple_sql(self, expression: exp.Tuple) -> str:
2109        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
2111    def update_sql(self, expression: exp.Update) -> str:
2112        this = self.sql(expression, "this")
2113        set_sql = self.expressions(expression, flat=True)
2114        from_sql = self.sql(expression, "from")
2115        where_sql = self.sql(expression, "where")
2116        returning = self.sql(expression, "returning")
2117        order = self.sql(expression, "order")
2118        limit = self.sql(expression, "limit")
2119        if self.RETURNING_END:
2120            expression_sql = f"{from_sql}{where_sql}{returning}"
2121        else:
2122            expression_sql = f"{returning}{from_sql}{where_sql}"
2123        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
2124        return self.prepend_ctes(expression, sql)
def values_sql( self, expression: sqlglot.expressions.Values, values_as_table: bool = True) -> str:
2126    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
2127        values_as_table = values_as_table and self.VALUES_AS_TABLE
2128
2129        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
2130        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
2131            args = self.expressions(expression)
2132            alias = self.sql(expression, "alias")
2133            values = f"VALUES{self.seg('')}{args}"
2134            values = (
2135                f"({values})"
2136                if self.WRAP_DERIVED_VALUES
2137                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
2138                else values
2139            )
2140            return f"{values} AS {alias}" if alias else values
2141
2142        # Converts `VALUES...` expression into a series of select unions.
2143        alias_node = expression.args.get("alias")
2144        column_names = alias_node and alias_node.columns
2145
2146        selects: t.List[exp.Query] = []
2147
2148        for i, tup in enumerate(expression.expressions):
2149            row = tup.expressions
2150
2151            if i == 0 and column_names:
2152                row = [
2153                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
2154                ]
2155
2156            selects.append(exp.Select(expressions=row))
2157
2158        if self.pretty:
2159            # This may result in poor performance for large-cardinality `VALUES` tables, due to
2160            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
2161            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
2162            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
2163            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
2164
2165        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
2166        unions = " UNION ALL ".join(self.sql(select) for select in selects)
2167        return f"({unions}){alias}"
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
2169    def var_sql(self, expression: exp.Var) -> str:
2170        return self.sql(expression, "this")
@unsupported_args('expressions')
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
2172    @unsupported_args("expressions")
2173    def into_sql(self, expression: exp.Into) -> str:
2174        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
2175        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
2176        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
2178    def from_sql(self, expression: exp.From) -> str:
2179        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
def groupingsets_sql(self, expression: sqlglot.expressions.GroupingSets) -> str:
2181    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
2182        grouping_sets = self.expressions(expression, indent=False)
2183        return f"GROUPING SETS {self.wrap(grouping_sets)}"
def rollup_sql(self, expression: sqlglot.expressions.Rollup) -> str:
2185    def rollup_sql(self, expression: exp.Rollup) -> str:
2186        expressions = self.expressions(expression, indent=False)
2187        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
def cube_sql(self, expression: sqlglot.expressions.Cube) -> str:
2189    def cube_sql(self, expression: exp.Cube) -> str:
2190        expressions = self.expressions(expression, indent=False)
2191        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
2193    def group_sql(self, expression: exp.Group) -> str:
2194        group_by_all = expression.args.get("all")
2195        if group_by_all is True:
2196            modifier = " ALL"
2197        elif group_by_all is False:
2198            modifier = " DISTINCT"
2199        else:
2200            modifier = ""
2201
2202        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
2203
2204        grouping_sets = self.expressions(expression, key="grouping_sets")
2205        cube = self.expressions(expression, key="cube")
2206        rollup = self.expressions(expression, key="rollup")
2207
2208        groupings = csv(
2209            self.seg(grouping_sets) if grouping_sets else "",
2210            self.seg(cube) if cube else "",
2211            self.seg(rollup) if rollup else "",
2212            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
2213            sep=self.GROUPINGS_SEP,
2214        )
2215
2216        if (
2217            expression.expressions
2218            and groupings
2219            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
2220        ):
2221            group_by = f"{group_by}{self.GROUPINGS_SEP}"
2222
2223        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
2225    def having_sql(self, expression: exp.Having) -> str:
2226        this = self.indent(self.sql(expression, "this"))
2227        return f"{self.seg('HAVING')}{self.sep()}{this}"
def connect_sql(self, expression: sqlglot.expressions.Connect) -> str:
2229    def connect_sql(self, expression: exp.Connect) -> str:
2230        start = self.sql(expression, "start")
2231        start = self.seg(f"START WITH {start}") if start else ""
2232        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
2233        connect = self.sql(expression, "connect")
2234        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
2235        return start + connect
def prior_sql(self, expression: sqlglot.expressions.Prior) -> str:
2237    def prior_sql(self, expression: exp.Prior) -> str:
2238        return f"PRIOR {self.sql(expression, 'this')}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
2240    def join_sql(self, expression: exp.Join) -> str:
2241        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
2242            side = None
2243        else:
2244            side = expression.side
2245
2246        op_sql = " ".join(
2247            op
2248            for op in (
2249                expression.method,
2250                "GLOBAL" if expression.args.get("global") else None,
2251                side,
2252                expression.kind,
2253                expression.hint if self.JOIN_HINTS else None,
2254            )
2255            if op
2256        )
2257        match_cond = self.sql(expression, "match_condition")
2258        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
2259        on_sql = self.sql(expression, "on")
2260        using = expression.args.get("using")
2261
2262        if not on_sql and using:
2263            on_sql = csv(*(self.sql(column) for column in using))
2264
2265        this = expression.this
2266        this_sql = self.sql(this)
2267
2268        exprs = self.expressions(expression)
2269        if exprs:
2270            this_sql = f"{this_sql},{self.seg(exprs)}"
2271
2272        if on_sql:
2273            on_sql = self.indent(on_sql, skip_first=True)
2274            space = self.seg(" " * self.pad) if self.pretty else " "
2275            if using:
2276                on_sql = f"{space}USING ({on_sql})"
2277            else:
2278                on_sql = f"{space}ON {on_sql}"
2279        elif not op_sql:
2280            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
2281                return f" {this_sql}"
2282
2283            return f", {this_sql}"
2284
2285        if op_sql != "STRAIGHT_JOIN":
2286            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
2287
2288        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str:
2290    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
2291        args = self.expressions(expression, flat=True)
2292        args = f"({args})" if len(args.split(",")) > 1 else args
2293        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_op(self, expression: sqlglot.expressions.Lateral) -> str:
2295    def lateral_op(self, expression: exp.Lateral) -> str:
2296        cross_apply = expression.args.get("cross_apply")
2297
2298        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
2299        if cross_apply is True:
2300            op = "INNER JOIN "
2301        elif cross_apply is False:
2302            op = "LEFT JOIN "
2303        else:
2304            op = ""
2305
2306        return f"{op}LATERAL"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
2308    def lateral_sql(self, expression: exp.Lateral) -> str:
2309        this = self.sql(expression, "this")
2310
2311        if expression.args.get("view"):
2312            alias = expression.args["alias"]
2313            columns = self.expressions(alias, key="columns", flat=True)
2314            table = f" {alias.name}" if alias.name else ""
2315            columns = f" AS {columns}" if columns else ""
2316            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
2317            return f"{op_sql}{self.sep()}{this}{table}{columns}"
2318
2319        alias = self.sql(expression, "alias")
2320        alias = f" AS {alias}" if alias else ""
2321
2322        ordinality = expression.args.get("ordinality") or ""
2323        if ordinality:
2324            ordinality = f" WITH ORDINALITY{alias}"
2325            alias = ""
2326
2327        return f"{self.lateral_op(expression)} {this}{alias}{ordinality}"
def limit_sql(self, expression: sqlglot.expressions.Limit, top: bool = False) -> str:
2329    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
2330        this = self.sql(expression, "this")
2331
2332        args = [
2333            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
2334            for e in (expression.args.get(k) for k in ("offset", "expression"))
2335            if e
2336        ]
2337
2338        args_sql = ", ".join(self.sql(e) for e in args)
2339        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
2340        expressions = self.expressions(expression, flat=True)
2341        limit_options = self.sql(expression, "limit_options")
2342        expressions = f" BY {expressions}" if expressions else ""
2343
2344        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{limit_options}{expressions}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
2346    def offset_sql(self, expression: exp.Offset) -> str:
2347        this = self.sql(expression, "this")
2348        value = expression.expression
2349        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
2350        expressions = self.expressions(expression, flat=True)
2351        expressions = f" BY {expressions}" if expressions else ""
2352        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str:
2354    def setitem_sql(self, expression: exp.SetItem) -> str:
2355        kind = self.sql(expression, "kind")
2356        kind = f"{kind} " if kind else ""
2357        this = self.sql(expression, "this")
2358        expressions = self.expressions(expression)
2359        collate = self.sql(expression, "collate")
2360        collate = f" COLLATE {collate}" if collate else ""
2361        global_ = "GLOBAL " if expression.args.get("global") else ""
2362        return f"{global_}{kind}{this}{expressions}{collate}"
def set_sql(self, expression: sqlglot.expressions.Set) -> str:
2364    def set_sql(self, expression: exp.Set) -> str:
2365        expressions = f" {self.expressions(expression, flat=True)}"
2366        tag = " TAG" if expression.args.get("tag") else ""
2367        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str:
2369    def pragma_sql(self, expression: exp.Pragma) -> str:
2370        return f"PRAGMA {self.sql(expression, 'this')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
2372    def lock_sql(self, expression: exp.Lock) -> str:
2373        if not self.LOCKING_READS_SUPPORTED:
2374            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
2375            return ""
2376
2377        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
2378        expressions = self.expressions(expression, flat=True)
2379        expressions = f" OF {expressions}" if expressions else ""
2380        wait = expression.args.get("wait")
2381
2382        if wait is not None:
2383            if isinstance(wait, exp.Literal):
2384                wait = f" WAIT {self.sql(wait)}"
2385            else:
2386                wait = " NOWAIT" if wait else " SKIP LOCKED"
2387
2388        return f"{lock_type}{expressions}{wait or ''}"
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
2390    def literal_sql(self, expression: exp.Literal) -> str:
2391        text = expression.this or ""
2392        if expression.is_string:
2393            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
2394        return text
def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2396    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
2397        if self.dialect.ESCAPED_SEQUENCES:
2398            to_escaped = self.dialect.ESCAPED_SEQUENCES
2399            text = "".join(
2400                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
2401            )
2402
2403        return self._replace_line_breaks(text).replace(
2404            self.dialect.QUOTE_END, self._escaped_quote_end
2405        )
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
2407    def loaddata_sql(self, expression: exp.LoadData) -> str:
2408        local = " LOCAL" if expression.args.get("local") else ""
2409        inpath = f" INPATH {self.sql(expression, 'inpath')}"
2410        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
2411        this = f" INTO TABLE {self.sql(expression, 'this')}"
2412        partition = self.sql(expression, "partition")
2413        partition = f" {partition}" if partition else ""
2414        input_format = self.sql(expression, "input_format")
2415        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
2416        serde = self.sql(expression, "serde")
2417        serde = f" SERDE {serde}" if serde else ""
2418        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
2420    def null_sql(self, *_) -> str:
2421        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
2423    def boolean_sql(self, expression: exp.Boolean) -> str:
2424        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
2426    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
2427        this = self.sql(expression, "this")
2428        this = f"{this} " if this else this
2429        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2430        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
def withfill_sql(self, expression: sqlglot.expressions.WithFill) -> str:
2432    def withfill_sql(self, expression: exp.WithFill) -> str:
2433        from_sql = self.sql(expression, "from")
2434        from_sql = f" FROM {from_sql}" if from_sql else ""
2435        to_sql = self.sql(expression, "to")
2436        to_sql = f" TO {to_sql}" if to_sql else ""
2437        step_sql = self.sql(expression, "step")
2438        step_sql = f" STEP {step_sql}" if step_sql else ""
2439        interpolated_values = [
2440            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
2441            if isinstance(e, exp.Alias)
2442            else self.sql(e, "this")
2443            for e in expression.args.get("interpolate") or []
2444        ]
2445        interpolate = (
2446            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2447        )
2448        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
2450    def cluster_sql(self, expression: exp.Cluster) -> str:
2451        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
2453    def distribute_sql(self, expression: exp.Distribute) -> str:
2454        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
2456    def sort_sql(self, expression: exp.Sort) -> str:
2457        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
2459    def ordered_sql(self, expression: exp.Ordered) -> str:
2460        desc = expression.args.get("desc")
2461        asc = not desc
2462
2463        nulls_first = expression.args.get("nulls_first")
2464        nulls_last = not nulls_first
2465        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2466        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2467        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2468
2469        this = self.sql(expression, "this")
2470
2471        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2472        nulls_sort_change = ""
2473        if nulls_first and (
2474            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2475        ):
2476            nulls_sort_change = " NULLS FIRST"
2477        elif (
2478            nulls_last
2479            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2480            and not nulls_are_last
2481        ):
2482            nulls_sort_change = " NULLS LAST"
2483
2484        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2485        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2486            window = expression.find_ancestor(exp.Window, exp.Select)
2487            if isinstance(window, exp.Window) and window.args.get("spec"):
2488                self.unsupported(
2489                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2490                )
2491                nulls_sort_change = ""
2492            elif self.NULL_ORDERING_SUPPORTED is False and (
2493                (asc and nulls_sort_change == " NULLS LAST")
2494                or (desc and nulls_sort_change == " NULLS FIRST")
2495            ):
2496                # BigQuery does not allow these ordering/nulls combinations when used under
2497                # an aggregation func or under a window containing one
2498                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
2499
2500                if isinstance(ancestor, exp.Window):
2501                    ancestor = ancestor.this
2502                if isinstance(ancestor, exp.AggFunc):
2503                    self.unsupported(
2504                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
2505                    )
2506                    nulls_sort_change = ""
2507            elif self.NULL_ORDERING_SUPPORTED is None:
2508                if expression.this.is_int:
2509                    self.unsupported(
2510                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2511                    )
2512                elif not isinstance(expression.this, exp.Rand):
2513                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2514                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2515                nulls_sort_change = ""
2516
2517        with_fill = self.sql(expression, "with_fill")
2518        with_fill = f" {with_fill}" if with_fill else ""
2519
2520        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
def matchrecognizemeasure_sql(self, expression: sqlglot.expressions.MatchRecognizeMeasure) -> str:
2522    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
2523        window_frame = self.sql(expression, "window_frame")
2524        window_frame = f"{window_frame} " if window_frame else ""
2525
2526        this = self.sql(expression, "this")
2527
2528        return f"{window_frame}{this}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
2530    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2531        partition = self.partition_by_sql(expression)
2532        order = self.sql(expression, "order")
2533        measures = self.expressions(expression, key="measures")
2534        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2535        rows = self.sql(expression, "rows")
2536        rows = self.seg(rows) if rows else ""
2537        after = self.sql(expression, "after")
2538        after = self.seg(after) if after else ""
2539        pattern = self.sql(expression, "pattern")
2540        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2541        definition_sqls = [
2542            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2543            for definition in expression.args.get("define", [])
2544        ]
2545        definitions = self.expressions(sqls=definition_sqls)
2546        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2547        body = "".join(
2548            (
2549                partition,
2550                order,
2551                measures,
2552                rows,
2553                after,
2554                pattern,
2555                define,
2556            )
2557        )
2558        alias = self.sql(expression, "alias")
2559        alias = f" {alias}" if alias else ""
2560        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
2562    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2563        limit = expression.args.get("limit")
2564
2565        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2566            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2567        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2568            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2569
2570        return csv(
2571            *sqls,
2572            *[self.sql(join) for join in expression.args.get("joins") or []],
2573            self.sql(expression, "match"),
2574            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2575            self.sql(expression, "prewhere"),
2576            self.sql(expression, "where"),
2577            self.sql(expression, "connect"),
2578            self.sql(expression, "group"),
2579            self.sql(expression, "having"),
2580            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2581            self.sql(expression, "order"),
2582            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
2583            *self.after_limit_modifiers(expression),
2584            self.options_modifier(expression),
2585            sep="",
2586        )
def options_modifier(self, expression: sqlglot.expressions.Expression) -> str:
2588    def options_modifier(self, expression: exp.Expression) -> str:
2589        options = self.expressions(expression, key="options")
2590        return f" {options}" if options else ""
def queryoption_sql(self, expression: sqlglot.expressions.QueryOption) -> str:
2592    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2593        self.unsupported("Unsupported query option.")
2594        return ""
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2596    def offset_limit_modifiers(
2597        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2598    ) -> t.List[str]:
2599        return [
2600            self.sql(expression, "offset") if fetch else self.sql(limit),
2601            self.sql(limit) if fetch else self.sql(expression, "offset"),
2602        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2604    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2605        locks = self.expressions(expression, key="locks", sep=" ")
2606        locks = f" {locks}" if locks else ""
2607        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
2609    def select_sql(self, expression: exp.Select) -> str:
2610        into = expression.args.get("into")
2611        if not self.SUPPORTS_SELECT_INTO and into:
2612            into.pop()
2613
2614        hint = self.sql(expression, "hint")
2615        distinct = self.sql(expression, "distinct")
2616        distinct = f" {distinct}" if distinct else ""
2617        kind = self.sql(expression, "kind")
2618
2619        limit = expression.args.get("limit")
2620        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
2621            top = self.limit_sql(limit, top=True)
2622            limit.pop()
2623        else:
2624            top = ""
2625
2626        expressions = self.expressions(expression)
2627
2628        if kind:
2629            if kind in self.SELECT_KINDS:
2630                kind = f" AS {kind}"
2631            else:
2632                if kind == "STRUCT":
2633                    expressions = self.expressions(
2634                        sqls=[
2635                            self.sql(
2636                                exp.Struct(
2637                                    expressions=[
2638                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2639                                        if isinstance(e, exp.Alias)
2640                                        else e
2641                                        for e in expression.expressions
2642                                    ]
2643                                )
2644                            )
2645                        ]
2646                    )
2647                kind = ""
2648
2649        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
2650        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
2651
2652        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2653        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2654        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2655        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2656        sql = self.query_modifiers(
2657            expression,
2658            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
2659            self.sql(expression, "into", comment=False),
2660            self.sql(expression, "from", comment=False),
2661        )
2662
2663        # If both the CTE and SELECT clauses have comments, generate the latter earlier
2664        if expression.args.get("with"):
2665            sql = self.maybe_comment(sql, expression)
2666            expression.pop_comments()
2667
2668        sql = self.prepend_ctes(expression, sql)
2669
2670        if not self.SUPPORTS_SELECT_INTO and into:
2671            if into.args.get("temporary"):
2672                table_kind = " TEMPORARY"
2673            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2674                table_kind = " UNLOGGED"
2675            else:
2676                table_kind = ""
2677            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2678
2679        return sql
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
2681    def schema_sql(self, expression: exp.Schema) -> str:
2682        this = self.sql(expression, "this")
2683        sql = self.schema_columns_sql(expression)
2684        return f"{this} {sql}" if this and sql else this or sql
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
2686    def schema_columns_sql(self, expression: exp.Schema) -> str:
2687        if expression.expressions:
2688            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2689        return ""
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
2691    def star_sql(self, expression: exp.Star) -> str:
2692        except_ = self.expressions(expression, key="except", flat=True)
2693        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
2694        replace = self.expressions(expression, key="replace", flat=True)
2695        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
2696        rename = self.expressions(expression, key="rename", flat=True)
2697        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
2698        return f"*{except_}{replace}{rename}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
2700    def parameter_sql(self, expression: exp.Parameter) -> str:
2701        this = self.sql(expression, "this")
2702        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
2704    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2705        this = self.sql(expression, "this")
2706        kind = expression.text("kind")
2707        if kind:
2708            kind = f"{kind}."
2709        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
2711    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2712        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
2714    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2715        alias = self.sql(expression, "alias")
2716        alias = f"{sep}{alias}" if alias else ""
2717        sample = self.sql(expression, "sample")
2718        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
2719            alias = f"{sample}{alias}"
2720
2721            # Set to None so it's not generated again by self.query_modifiers()
2722            expression.set("sample", None)
2723
2724        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
2725        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2726        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
2728    def qualify_sql(self, expression: exp.Qualify) -> str:
2729        this = self.indent(self.sql(expression, "this"))
2730        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
2732    def unnest_sql(self, expression: exp.Unnest) -> str:
2733        args = self.expressions(expression, flat=True)
2734
2735        alias = expression.args.get("alias")
2736        offset = expression.args.get("offset")
2737
2738        if self.UNNEST_WITH_ORDINALITY:
2739            if alias and isinstance(offset, exp.Expression):
2740                alias.append("columns", offset)
2741
2742        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2743            columns = alias.columns
2744            alias = self.sql(columns[0]) if columns else ""
2745        else:
2746            alias = self.sql(alias)
2747
2748        alias = f" AS {alias}" if alias else alias
2749        if self.UNNEST_WITH_ORDINALITY:
2750            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2751        else:
2752            if isinstance(offset, exp.Expression):
2753                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2754            elif offset:
2755                suffix = f"{alias} WITH OFFSET"
2756            else:
2757                suffix = alias
2758
2759        return f"UNNEST({args}){suffix}"
def prewhere_sql(self, expression: sqlglot.expressions.PreWhere) -> str:
2761    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2762        return ""
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
2764    def where_sql(self, expression: exp.Where) -> str:
2765        this = self.indent(self.sql(expression, "this"))
2766        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
2768    def window_sql(self, expression: exp.Window) -> str:
2769        this = self.sql(expression, "this")
2770        partition = self.partition_by_sql(expression)
2771        order = expression.args.get("order")
2772        order = self.order_sql(order, flat=True) if order else ""
2773        spec = self.sql(expression, "spec")
2774        alias = self.sql(expression, "alias")
2775        over = self.sql(expression, "over") or "OVER"
2776
2777        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2778
2779        first = expression.args.get("first")
2780        if first is None:
2781            first = ""
2782        else:
2783            first = "FIRST" if first else "LAST"
2784
2785        if not partition and not order and not spec and alias:
2786            return f"{this} {alias}"
2787
2788        args = self.format_args(
2789            *[arg for arg in (alias, first, partition, order, spec) if arg], sep=" "
2790        )
2791        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2793    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2794        partition = self.expressions(expression, key="partition_by", flat=True)
2795        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
2797    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2798        kind = self.sql(expression, "kind")
2799        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2800        end = (
2801            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2802            or "CURRENT ROW"
2803        )
2804        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
2806    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2807        this = self.sql(expression, "this")
2808        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2809        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
2811    def between_sql(self, expression: exp.Between) -> str:
2812        this = self.sql(expression, "this")
2813        low = self.sql(expression, "low")
2814        high = self.sql(expression, "high")
2815        return f"{this} BETWEEN {low} AND {high}"
def bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket, index_offset: Optional[int] = None) -> List[sqlglot.expressions.Expression]:
2817    def bracket_offset_expressions(
2818        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
2819    ) -> t.List[exp.Expression]:
2820        return apply_index_offset(
2821            expression.this,
2822            expression.expressions,
2823            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
2824            dialect=self.dialect,
2825        )
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
2827    def bracket_sql(self, expression: exp.Bracket) -> str:
2828        expressions = self.bracket_offset_expressions(expression)
2829        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2830        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
2832    def all_sql(self, expression: exp.All) -> str:
2833        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
2835    def any_sql(self, expression: exp.Any) -> str:
2836        this = self.sql(expression, "this")
2837        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
2838            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2839                this = self.wrap(this)
2840            return f"ANY{this}"
2841        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
2843    def exists_sql(self, expression: exp.Exists) -> str:
2844        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
2846    def case_sql(self, expression: exp.Case) -> str:
2847        this = self.sql(expression, "this")
2848        statements = [f"CASE {this}" if this else "CASE"]
2849
2850        for e in expression.args["ifs"]:
2851            statements.append(f"WHEN {self.sql(e, 'this')}")
2852            statements.append(f"THEN {self.sql(e, 'true')}")
2853
2854        default = self.sql(expression, "default")
2855
2856        if default:
2857            statements.append(f"ELSE {default}")
2858
2859        statements.append("END")
2860
2861        if self.pretty and self.too_wide(statements):
2862            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2863
2864        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
2866    def constraint_sql(self, expression: exp.Constraint) -> str:
2867        this = self.sql(expression, "this")
2868        expressions = self.expressions(expression, flat=True)
2869        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
2871    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2872        order = expression.args.get("order")
2873        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2874        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
2876    def extract_sql(self, expression: exp.Extract) -> str:
2877        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2878        expression_sql = self.sql(expression, "expression")
2879        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
2881    def trim_sql(self, expression: exp.Trim) -> str:
2882        trim_type = self.sql(expression, "position")
2883
2884        if trim_type == "LEADING":
2885            func_name = "LTRIM"
2886        elif trim_type == "TRAILING":
2887            func_name = "RTRIM"
2888        else:
2889            func_name = "TRIM"
2890
2891        return self.func(func_name, expression.this, expression.expression)
def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2893    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2894        args = expression.expressions
2895        if isinstance(expression, exp.ConcatWs):
2896            args = args[1:]  # Skip the delimiter
2897
2898        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2899            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
2900
2901        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2902            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2903
2904        return args
def concat_sql(self, expression: sqlglot.expressions.Concat) -> str:
2906    def concat_sql(self, expression: exp.Concat) -> str:
2907        expressions = self.convert_concat_args(expression)
2908
2909        # Some dialects don't allow a single-argument CONCAT call
2910        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2911            return self.sql(expressions[0])
2912
2913        return self.func("CONCAT", *expressions)
def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str:
2915    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2916        return self.func(
2917            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2918        )
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
2920    def check_sql(self, expression: exp.Check) -> str:
2921        this = self.sql(expression, key="this")
2922        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2924    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2925        expressions = self.expressions(expression, flat=True)
2926        expressions = f" ({expressions})" if expressions else ""
2927        reference = self.sql(expression, "reference")
2928        reference = f" {reference}" if reference else ""
2929        delete = self.sql(expression, "delete")
2930        delete = f" ON DELETE {delete}" if delete else ""
2931        update = self.sql(expression, "update")
2932        update = f" ON UPDATE {update}" if update else ""
2933        options = self.expressions(expression, key="options", flat=True, sep=" ")
2934        options = f" {options}" if options else ""
2935        return f"FOREIGN KEY{expressions}{reference}{delete}{update}{options}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2937    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2938        expressions = self.expressions(expression, flat=True)
2939        options = self.expressions(expression, key="options", flat=True, sep=" ")
2940        options = f" {options}" if options else ""
2941        return f"PRIMARY KEY ({expressions}){options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
2943    def if_sql(self, expression: exp.If) -> str:
2944        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
2946    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2947        modifier = expression.args.get("modifier")
2948        modifier = f" {modifier}" if modifier else ""
2949        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
2951    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2952        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
def jsonpath_sql(self, expression: sqlglot.expressions.JSONPath) -> str:
2954    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2955        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2956
2957        if expression.args.get("escape"):
2958            path = self.escape_str(path)
2959
2960        if self.QUOTE_JSON_PATH:
2961            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2962
2963        return path
def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str:
2965    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2966        if isinstance(expression, exp.JSONPathPart):
2967            transform = self.TRANSFORMS.get(expression.__class__)
2968            if not callable(transform):
2969                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2970                return ""
2971
2972            return transform(self, expression)
2973
2974        if isinstance(expression, int):
2975            return str(expression)
2976
2977        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2978            escaped = expression.replace("'", "\\'")
2979            escaped = f"\\'{expression}\\'"
2980        else:
2981            escaped = expression.replace('"', '\\"')
2982            escaped = f'"{escaped}"'
2983
2984        return escaped
def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str:
2986    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2987        return f"{self.sql(expression, 'this')} FORMAT JSON"
def jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2989    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2990        null_handling = expression.args.get("null_handling")
2991        null_handling = f" {null_handling}" if null_handling else ""
2992
2993        unique_keys = expression.args.get("unique_keys")
2994        if unique_keys is not None:
2995            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2996        else:
2997            unique_keys = ""
2998
2999        return_type = self.sql(expression, "return_type")
3000        return_type = f" RETURNING {return_type}" if return_type else ""
3001        encoding = self.sql(expression, "encoding")
3002        encoding = f" ENCODING {encoding}" if encoding else ""
3003
3004        return self.func(
3005            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
3006            *expression.expressions,
3007            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
3008        )
def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str:
3010    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
3011        return self.jsonobject_sql(expression)
def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str:
3013    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
3014        null_handling = expression.args.get("null_handling")
3015        null_handling = f" {null_handling}" if null_handling else ""
3016        return_type = self.sql(expression, "return_type")
3017        return_type = f" RETURNING {return_type}" if return_type else ""
3018        strict = " STRICT" if expression.args.get("strict") else ""
3019        return self.func(
3020            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
3021        )
def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str:
3023    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
3024        this = self.sql(expression, "this")
3025        order = self.sql(expression, "order")
3026        null_handling = expression.args.get("null_handling")
3027        null_handling = f" {null_handling}" if null_handling else ""
3028        return_type = self.sql(expression, "return_type")
3029        return_type = f" RETURNING {return_type}" if return_type else ""
3030        strict = " STRICT" if expression.args.get("strict") else ""
3031        return self.func(
3032            "JSON_ARRAYAGG",
3033            this,
3034            suffix=f"{order}{null_handling}{return_type}{strict})",
3035        )
def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str:
3037    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
3038        path = self.sql(expression, "path")
3039        path = f" PATH {path}" if path else ""
3040        nested_schema = self.sql(expression, "nested_schema")
3041
3042        if nested_schema:
3043            return f"NESTED{path} {nested_schema}"
3044
3045        this = self.sql(expression, "this")
3046        kind = self.sql(expression, "kind")
3047        kind = f" {kind}" if kind else ""
3048        return f"{this}{kind}{path}"
def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str:
3050    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
3051        return self.func("COLUMNS", *expression.expressions)
def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str:
3053    def jsontable_sql(self, expression: exp.JSONTable) -> str:
3054        this = self.sql(expression, "this")
3055        path = self.sql(expression, "path")
3056        path = f", {path}" if path else ""
3057        error_handling = expression.args.get("error_handling")
3058        error_handling = f" {error_handling}" if error_handling else ""
3059        empty_handling = expression.args.get("empty_handling")
3060        empty_handling = f" {empty_handling}" if empty_handling else ""
3061        schema = self.sql(expression, "schema")
3062        return self.func(
3063            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
3064        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
3066    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
3067        this = self.sql(expression, "this")
3068        kind = self.sql(expression, "kind")
3069        path = self.sql(expression, "path")
3070        path = f" {path}" if path else ""
3071        as_json = " AS JSON" if expression.args.get("as_json") else ""
3072        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
3074    def openjson_sql(self, expression: exp.OpenJSON) -> str:
3075        this = self.sql(expression, "this")
3076        path = self.sql(expression, "path")
3077        path = f", {path}" if path else ""
3078        expressions = self.expressions(expression)
3079        with_ = (
3080            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
3081            if expressions
3082            else ""
3083        )
3084        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
3086    def in_sql(self, expression: exp.In) -> str:
3087        query = expression.args.get("query")
3088        unnest = expression.args.get("unnest")
3089        field = expression.args.get("field")
3090        is_global = " GLOBAL" if expression.args.get("is_global") else ""
3091
3092        if query:
3093            in_sql = self.sql(query)
3094        elif unnest:
3095            in_sql = self.in_unnest_op(unnest)
3096        elif field:
3097            in_sql = self.sql(field)
3098        else:
3099            in_sql = f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
3100
3101        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
3103    def in_unnest_op(self, unnest: exp.Unnest) -> str:
3104        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
3106    def interval_sql(self, expression: exp.Interval) -> str:
3107        unit = self.sql(expression, "unit")
3108        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
3109            unit = self.TIME_PART_SINGULARS.get(unit, unit)
3110        unit = f" {unit}" if unit else ""
3111
3112        if self.SINGLE_STRING_INTERVAL:
3113            this = expression.this.name if expression.this else ""
3114            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
3115
3116        this = self.sql(expression, "this")
3117        if this:
3118            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
3119            this = f" {this}" if unwrapped else f" ({this})"
3120
3121        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
3123    def return_sql(self, expression: exp.Return) -> str:
3124        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
3126    def reference_sql(self, expression: exp.Reference) -> str:
3127        this = self.sql(expression, "this")
3128        expressions = self.expressions(expression, flat=True)
3129        expressions = f"({expressions})" if expressions else ""
3130        options = self.expressions(expression, key="options", flat=True, sep=" ")
3131        options = f" {options}" if options else ""
3132        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
3134    def anonymous_sql(self, expression: exp.Anonymous) -> str:
3135        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
3136        parent = expression.parent
3137        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
3138        return self.func(
3139            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
3140        )
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
3142    def paren_sql(self, expression: exp.Paren) -> str:
3143        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
3144        return f"({sql}{self.seg(')', sep='')}"
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
3146    def neg_sql(self, expression: exp.Neg) -> str:
3147        # This makes sure we don't convert "- - 5" to "--5", which is a comment
3148        this_sql = self.sql(expression, "this")
3149        sep = " " if this_sql[0] == "-" else ""
3150        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
3152    def not_sql(self, expression: exp.Not) -> str:
3153        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
3155    def alias_sql(self, expression: exp.Alias) -> str:
3156        alias = self.sql(expression, "alias")
3157        alias = f" AS {alias}" if alias else ""
3158        return f"{self.sql(expression, 'this')}{alias}"
def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str:
3160    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
3161        alias = expression.args["alias"]
3162
3163        parent = expression.parent
3164        pivot = parent and parent.parent
3165
3166        if isinstance(pivot, exp.Pivot) and pivot.unpivot:
3167            identifier_alias = isinstance(alias, exp.Identifier)
3168            literal_alias = isinstance(alias, exp.Literal)
3169
3170            if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3171                alias.replace(exp.Literal.string(alias.output_name))
3172            elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
3173                alias.replace(exp.to_identifier(alias.output_name))
3174
3175        return self.alias_sql(expression)
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
3177    def aliases_sql(self, expression: exp.Aliases) -> str:
3178        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def atindex_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
3180    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
3181        this = self.sql(expression, "this")
3182        index = self.sql(expression, "expression")
3183        return f"{this} AT {index}"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
3185    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
3186        this = self.sql(expression, "this")
3187        zone = self.sql(expression, "zone")
3188        return f"{this} AT TIME ZONE {zone}"
def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str:
3190    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
3191        this = self.sql(expression, "this")
3192        zone = self.sql(expression, "zone")
3193        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
3195    def add_sql(self, expression: exp.Add) -> str:
3196        return self.binary(expression, "+")
def and_sql( self, expression: sqlglot.expressions.And, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3198    def and_sql(
3199        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
3200    ) -> str:
3201        return self.connector_sql(expression, "AND", stack)
def or_sql( self, expression: sqlglot.expressions.Or, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3203    def or_sql(
3204        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
3205    ) -> str:
3206        return self.connector_sql(expression, "OR", stack)
def xor_sql( self, expression: sqlglot.expressions.Xor, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3208    def xor_sql(
3209        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
3210    ) -> str:
3211        return self.connector_sql(expression, "XOR", stack)
def connector_sql( self, expression: sqlglot.expressions.Connector, op: str, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
3213    def connector_sql(
3214        self,
3215        expression: exp.Connector,
3216        op: str,
3217        stack: t.Optional[t.List[str | exp.Expression]] = None,
3218    ) -> str:
3219        if stack is not None:
3220            if expression.expressions:
3221                stack.append(self.expressions(expression, sep=f" {op} "))
3222            else:
3223                stack.append(expression.right)
3224                if expression.comments and self.comments:
3225                    for comment in expression.comments:
3226                        if comment:
3227                            op += f" /*{self.pad_comment(comment)}*/"
3228                stack.extend((op, expression.left))
3229            return op
3230
3231        stack = [expression]
3232        sqls: t.List[str] = []
3233        ops = set()
3234
3235        while stack:
3236            node = stack.pop()
3237            if isinstance(node, exp.Connector):
3238                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
3239            else:
3240                sql = self.sql(node)
3241                if sqls and sqls[-1] in ops:
3242                    sqls[-1] += f" {sql}"
3243                else:
3244                    sqls.append(sql)
3245
3246        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
3247        return sep.join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
3249    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
3250        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
3252    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
3253        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
3255    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
3256        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
3258    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
3259        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
3261    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
3262        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
3264    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
3265        return self.binary(expression, "^")
def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
3267    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
3268        format_sql = self.sql(expression, "format")
3269        format_sql = f" FORMAT {format_sql}" if format_sql else ""
3270        to_sql = self.sql(expression, "to")
3271        to_sql = f" {to_sql}" if to_sql else ""
3272        action = self.sql(expression, "action")
3273        action = f" {action}" if action else ""
3274        default = self.sql(expression, "default")
3275        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
3276        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
3278    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
3279        zone = self.sql(expression, "this")
3280        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
3282    def collate_sql(self, expression: exp.Collate) -> str:
3283        if self.COLLATE_IS_FUNC:
3284            return self.function_fallback_sql(expression)
3285        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
3287    def command_sql(self, expression: exp.Command) -> str:
3288        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
3290    def comment_sql(self, expression: exp.Comment) -> str:
3291        this = self.sql(expression, "this")
3292        kind = expression.args["kind"]
3293        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
3294        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
3295        expression_sql = self.sql(expression, "expression")
3296        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
3298    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
3299        this = self.sql(expression, "this")
3300        delete = " DELETE" if expression.args.get("delete") else ""
3301        recompress = self.sql(expression, "recompress")
3302        recompress = f" RECOMPRESS {recompress}" if recompress else ""
3303        to_disk = self.sql(expression, "to_disk")
3304        to_disk = f" TO DISK {to_disk}" if to_disk else ""
3305        to_volume = self.sql(expression, "to_volume")
3306        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
3307        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
3309    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
3310        where = self.sql(expression, "where")
3311        group = self.sql(expression, "group")
3312        aggregates = self.expressions(expression, key="aggregates")
3313        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
3314
3315        if not (where or group or aggregates) and len(expression.expressions) == 1:
3316            return f"TTL {self.expressions(expression, flat=True)}"
3317
3318        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
3320    def transaction_sql(self, expression: exp.Transaction) -> str:
3321        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
3323    def commit_sql(self, expression: exp.Commit) -> str:
3324        chain = expression.args.get("chain")
3325        if chain is not None:
3326            chain = " AND CHAIN" if chain else " AND NO CHAIN"
3327
3328        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
3330    def rollback_sql(self, expression: exp.Rollback) -> str:
3331        savepoint = expression.args.get("savepoint")
3332        savepoint = f" TO {savepoint}" if savepoint else ""
3333        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
3335    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
3336        this = self.sql(expression, "this")
3337
3338        dtype = self.sql(expression, "dtype")
3339        if dtype:
3340            collate = self.sql(expression, "collate")
3341            collate = f" COLLATE {collate}" if collate else ""
3342            using = self.sql(expression, "using")
3343            using = f" USING {using}" if using else ""
3344            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
3345
3346        default = self.sql(expression, "default")
3347        if default:
3348            return f"ALTER COLUMN {this} SET DEFAULT {default}"
3349
3350        comment = self.sql(expression, "comment")
3351        if comment:
3352            return f"ALTER COLUMN {this} COMMENT {comment}"
3353
3354        visible = expression.args.get("visible")
3355        if visible:
3356            return f"ALTER COLUMN {this} SET {visible}"
3357
3358        allow_null = expression.args.get("allow_null")
3359        drop = expression.args.get("drop")
3360
3361        if not drop and not allow_null:
3362            self.unsupported("Unsupported ALTER COLUMN syntax")
3363
3364        if allow_null is not None:
3365            keyword = "DROP" if drop else "SET"
3366            return f"ALTER COLUMN {this} {keyword} NOT NULL"
3367
3368        return f"ALTER COLUMN {this} DROP DEFAULT"
def alterindex_sql(self, expression: sqlglot.expressions.AlterIndex) -> str:
3370    def alterindex_sql(self, expression: exp.AlterIndex) -> str:
3371        this = self.sql(expression, "this")
3372
3373        visible = expression.args.get("visible")
3374        visible_sql = "VISIBLE" if visible else "INVISIBLE"
3375
3376        return f"ALTER INDEX {this} {visible_sql}"
def alterdiststyle_sql(self, expression: sqlglot.expressions.AlterDistStyle) -> str:
3378    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
3379        this = self.sql(expression, "this")
3380        if not isinstance(expression.this, exp.Var):
3381            this = f"KEY DISTKEY {this}"
3382        return f"ALTER DISTSTYLE {this}"
def altersortkey_sql(self, expression: sqlglot.expressions.AlterSortKey) -> str:
3384    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
3385        compound = " COMPOUND" if expression.args.get("compound") else ""
3386        this = self.sql(expression, "this")
3387        expressions = self.expressions(expression, flat=True)
3388        expressions = f"({expressions})" if expressions else ""
3389        return f"ALTER{compound} SORTKEY {this or expressions}"
def alterrename_sql(self, expression: sqlglot.expressions.AlterRename) -> str:
3391    def alterrename_sql(self, expression: exp.AlterRename) -> str:
3392        if not self.RENAME_TABLE_WITH_DB:
3393            # Remove db from tables
3394            expression = expression.transform(
3395                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
3396            ).assert_is(exp.AlterRename)
3397        this = self.sql(expression, "this")
3398        return f"RENAME TO {this}"
def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str:
3400    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
3401        exists = " IF EXISTS" if expression.args.get("exists") else ""
3402        old_column = self.sql(expression, "this")
3403        new_column = self.sql(expression, "to")
3404        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
def alterset_sql(self, expression: sqlglot.expressions.AlterSet) -> str:
3406    def alterset_sql(self, expression: exp.AlterSet) -> str:
3407        exprs = self.expressions(expression, flat=True)
3408        return f"SET {exprs}"
def alter_sql(self, expression: sqlglot.expressions.Alter) -> str:
3410    def alter_sql(self, expression: exp.Alter) -> str:
3411        actions = expression.args["actions"]
3412
3413        if isinstance(actions[0], exp.ColumnDef):
3414            actions = self.add_column_sql(expression)
3415        elif isinstance(actions[0], exp.Schema):
3416            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
3417        elif isinstance(actions[0], exp.Delete):
3418            actions = self.expressions(expression, key="actions", flat=True)
3419        elif isinstance(actions[0], exp.Query):
3420            actions = "AS " + self.expressions(expression, key="actions")
3421        else:
3422            actions = self.expressions(expression, key="actions", flat=True)
3423
3424        exists = " IF EXISTS" if expression.args.get("exists") else ""
3425        on_cluster = self.sql(expression, "cluster")
3426        on_cluster = f" {on_cluster}" if on_cluster else ""
3427        only = " ONLY" if expression.args.get("only") else ""
3428        options = self.expressions(expression, key="options")
3429        options = f", {options}" if options else ""
3430        kind = self.sql(expression, "kind")
3431        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
3432
3433        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
def add_column_sql(self, expression: sqlglot.expressions.Alter) -> str:
3435    def add_column_sql(self, expression: exp.Alter) -> str:
3436        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
3437            return self.expressions(
3438                expression,
3439                key="actions",
3440                prefix="ADD COLUMN ",
3441                skip_first=True,
3442            )
3443        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
3445    def droppartition_sql(self, expression: exp.DropPartition) -> str:
3446        expressions = self.expressions(expression)
3447        exists = " IF EXISTS " if expression.args.get("exists") else " "
3448        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
3450    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
3451        return f"ADD {self.expressions(expression)}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
3453    def distinct_sql(self, expression: exp.Distinct) -> str:
3454        this = self.expressions(expression, flat=True)
3455
3456        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
3457            case = exp.case()
3458            for arg in expression.expressions:
3459                case = case.when(arg.is_(exp.null()), exp.null())
3460            this = self.sql(case.else_(f"({this})"))
3461
3462        this = f" {this}" if this else ""
3463
3464        on = self.sql(expression, "on")
3465        on = f" ON {on}" if on else ""
3466        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
3468    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
3469        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
3471    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
3472        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str:
3474    def havingmax_sql(self, expression: exp.HavingMax) -> str:
3475        this_sql = self.sql(expression, "this")
3476        expression_sql = self.sql(expression, "expression")
3477        kind = "MAX" if expression.args.get("max") else "MIN"
3478        return f"{this_sql} HAVING {kind} {expression_sql}"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
3480    def intdiv_sql(self, expression: exp.IntDiv) -> str:
3481        return self.sql(
3482            exp.Cast(
3483                this=exp.Div(this=expression.this, expression=expression.expression),
3484                to=exp.DataType(this=exp.DataType.Type.INT),
3485            )
3486        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
3488    def dpipe_sql(self, expression: exp.DPipe) -> str:
3489        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
3490            return self.func(
3491                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
3492            )
3493        return self.binary(expression, "||")
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
3495    def div_sql(self, expression: exp.Div) -> str:
3496        l, r = expression.left, expression.right
3497
3498        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
3499            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
3500
3501        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
3502            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
3503                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
3504
3505        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
3506            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
3507                return self.sql(
3508                    exp.cast(
3509                        l / r,
3510                        to=exp.DataType.Type.BIGINT,
3511                    )
3512                )
3513
3514        return self.binary(expression, "/")
def safedivide_sql(self, expression: sqlglot.expressions.SafeDivide) -> str:
3516    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
3517        n = exp._wrap(expression.this, exp.Binary)
3518        d = exp._wrap(expression.expression, exp.Binary)
3519        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
3521    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3522        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
3524    def distance_sql(self, expression: exp.Distance) -> str:
3525        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
3527    def dot_sql(self, expression: exp.Dot) -> str:
3528        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
3530    def eq_sql(self, expression: exp.EQ) -> str:
3531        return self.binary(expression, "=")
def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str:
3533    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3534        return self.binary(expression, ":=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
3536    def escape_sql(self, expression: exp.Escape) -> str:
3537        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
3539    def glob_sql(self, expression: exp.Glob) -> str:
3540        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
3542    def gt_sql(self, expression: exp.GT) -> str:
3543        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
3545    def gte_sql(self, expression: exp.GTE) -> str:
3546        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
3548    def ilike_sql(self, expression: exp.ILike) -> str:
3549        return self.binary(expression, "ILIKE")
def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str:
3551    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
3552        return self.binary(expression, "ILIKE ANY")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
3554    def is_sql(self, expression: exp.Is) -> str:
3555        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3556            return self.sql(
3557                expression.this if expression.expression.this else exp.not_(expression.this)
3558            )
3559        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
3561    def like_sql(self, expression: exp.Like) -> str:
3562        return self.binary(expression, "LIKE")
def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str:
3564    def likeany_sql(self, expression: exp.LikeAny) -> str:
3565        return self.binary(expression, "LIKE ANY")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
3567    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3568        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
3570    def lt_sql(self, expression: exp.LT) -> str:
3571        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
3573    def lte_sql(self, expression: exp.LTE) -> str:
3574        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
3576    def mod_sql(self, expression: exp.Mod) -> str:
3577        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
3579    def mul_sql(self, expression: exp.Mul) -> str:
3580        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
3582    def neq_sql(self, expression: exp.NEQ) -> str:
3583        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
3585    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3586        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
3588    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3589        return self.binary(expression, "IS DISTINCT FROM")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
3591    def slice_sql(self, expression: exp.Slice) -> str:
3592        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
3594    def sub_sql(self, expression: exp.Sub) -> str:
3595        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
3597    def trycast_sql(self, expression: exp.TryCast) -> str:
3598        return self.cast_sql(expression, safe_prefix="TRY_")
def jsoncast_sql(self, expression: sqlglot.expressions.JSONCast) -> str:
3600    def jsoncast_sql(self, expression: exp.JSONCast) -> str:
3601        return self.cast_sql(expression)
def try_sql(self, expression: sqlglot.expressions.Try) -> str:
3603    def try_sql(self, expression: exp.Try) -> str:
3604        if not self.TRY_SUPPORTED:
3605            self.unsupported("Unsupported TRY function")
3606            return self.sql(expression, "this")
3607
3608        return self.func("TRY", expression.this)
def log_sql(self, expression: sqlglot.expressions.Log) -> str:
3610    def log_sql(self, expression: exp.Log) -> str:
3611        this = expression.this
3612        expr = expression.expression
3613
3614        if self.dialect.LOG_BASE_FIRST is False:
3615            this, expr = expr, this
3616        elif self.dialect.LOG_BASE_FIRST is None and expr:
3617            if this.name in ("2", "10"):
3618                return self.func(f"LOG{this.name}", expr)
3619
3620            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
3621
3622        return self.func("LOG", this, expr)
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
3624    def use_sql(self, expression: exp.Use) -> str:
3625        kind = self.sql(expression, "kind")
3626        kind = f" {kind}" if kind else ""
3627        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
3628        this = f" {this}" if this else ""
3629        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
3631    def binary(self, expression: exp.Binary, op: str) -> str:
3632        sqls: t.List[str] = []
3633        stack: t.List[t.Union[str, exp.Expression]] = [expression]
3634        binary_type = type(expression)
3635
3636        while stack:
3637            node = stack.pop()
3638
3639            if type(node) is binary_type:
3640                op_func = node.args.get("operator")
3641                if op_func:
3642                    op = f"OPERATOR({self.sql(op_func)})"
3643
3644                stack.append(node.right)
3645                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
3646                stack.append(node.left)
3647            else:
3648                sqls.append(self.sql(node))
3649
3650        return "".join(sqls)
def ceil_floor( self, expression: sqlglot.expressions.Ceil | sqlglot.expressions.Floor) -> str:
3652    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
3653        to_clause = self.sql(expression, "to")
3654        if to_clause:
3655            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
3656
3657        return self.function_fallback_sql(expression)
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
3659    def function_fallback_sql(self, expression: exp.Func) -> str:
3660        args = []
3661
3662        for key in expression.arg_types:
3663            arg_value = expression.args.get(key)
3664
3665            if isinstance(arg_value, list):
3666                for value in arg_value:
3667                    args.append(value)
3668            elif arg_value is not None:
3669                args.append(arg_value)
3670
3671        if self.dialect.PRESERVE_ORIGINAL_NAMES:
3672            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3673        else:
3674            name = expression.sql_name()
3675
3676        return self.func(name, *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')', normalize: bool = True) -> str:
3678    def func(
3679        self,
3680        name: str,
3681        *args: t.Optional[exp.Expression | str],
3682        prefix: str = "(",
3683        suffix: str = ")",
3684        normalize: bool = True,
3685    ) -> str:
3686        name = self.normalize_func(name) if normalize else name
3687        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
def format_args( self, *args: Union[str, sqlglot.expressions.Expression, NoneType], sep: str = ', ') -> str:
3689    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
3690        arg_sqls = tuple(
3691            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
3692        )
3693        if self.pretty and self.too_wide(arg_sqls):
3694            return self.indent(
3695                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
3696            )
3697        return sep.join(arg_sqls)
def too_wide(self, args: Iterable) -> bool:
3699    def too_wide(self, args: t.Iterable) -> bool:
3700        return sum(len(arg) for arg in args) > self.max_text_width
def format_time( self, expression: sqlglot.expressions.Expression, inverse_time_mapping: Optional[Dict[str, str]] = None, inverse_time_trie: Optional[Dict] = None) -> Optional[str]:
3702    def format_time(
3703        self,
3704        expression: exp.Expression,
3705        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
3706        inverse_time_trie: t.Optional[t.Dict] = None,
3707    ) -> t.Optional[str]:
3708        return format_time(
3709            self.sql(expression, "format"),
3710            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
3711            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
3712        )
def expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[Collection[Union[str, sqlglot.expressions.Expression]]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, skip_last: bool = False, sep: str = ', ', prefix: str = '', dynamic: bool = False, new_line: bool = False) -> str:
3714    def expressions(
3715        self,
3716        expression: t.Optional[exp.Expression] = None,
3717        key: t.Optional[str] = None,
3718        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3719        flat: bool = False,
3720        indent: bool = True,
3721        skip_first: bool = False,
3722        skip_last: bool = False,
3723        sep: str = ", ",
3724        prefix: str = "",
3725        dynamic: bool = False,
3726        new_line: bool = False,
3727    ) -> str:
3728        expressions = expression.args.get(key or "expressions") if expression else sqls
3729
3730        if not expressions:
3731            return ""
3732
3733        if flat:
3734            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3735
3736        num_sqls = len(expressions)
3737        result_sqls = []
3738
3739        for i, e in enumerate(expressions):
3740            sql = self.sql(e, comment=False)
3741            if not sql:
3742                continue
3743
3744            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3745
3746            if self.pretty:
3747                if self.leading_comma:
3748                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
3749                else:
3750                    result_sqls.append(
3751                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
3752                    )
3753            else:
3754                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3755
3756        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
3757            if new_line:
3758                result_sqls.insert(0, "")
3759                result_sqls.append("")
3760            result_sql = "\n".join(s.rstrip() for s in result_sqls)
3761        else:
3762            result_sql = "".join(result_sqls)
3763
3764        return (
3765            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
3766            if indent
3767            else result_sql
3768        )
def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3770    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3771        flat = flat or isinstance(expression.parent, exp.Properties)
3772        expressions_sql = self.expressions(expression, flat=flat)
3773        if flat:
3774            return f"{op} {expressions_sql}"
3775        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
3777    def naked_property(self, expression: exp.Property) -> str:
3778        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3779        if not property_name:
3780            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3781        return f"{property_name} {self.sql(expression, 'this')}"
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
3783    def tag_sql(self, expression: exp.Tag) -> str:
3784        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
3786    def token_sql(self, token_type: TokenType) -> str:
3787        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
3789    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3790        this = self.sql(expression, "this")
3791        expressions = self.no_identify(self.expressions, expression)
3792        expressions = (
3793            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3794        )
3795        return f"{this}{expressions}" if expressions.strip() != "" else this
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
3797    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3798        this = self.sql(expression, "this")
3799        expressions = self.expressions(expression, flat=True)
3800        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
3802    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3803        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
3805    def when_sql(self, expression: exp.When) -> str:
3806        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3807        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3808        condition = self.sql(expression, "condition")
3809        condition = f" AND {condition}" if condition else ""
3810
3811        then_expression = expression.args.get("then")
3812        if isinstance(then_expression, exp.Insert):
3813            this = self.sql(then_expression, "this")
3814            this = f"INSERT {this}" if this else "INSERT"
3815            then = self.sql(then_expression, "expression")
3816            then = f"{this} VALUES {then}" if then else this
3817        elif isinstance(then_expression, exp.Update):
3818            if isinstance(then_expression.args.get("expressions"), exp.Star):
3819                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3820            else:
3821                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3822        else:
3823            then = self.sql(then_expression)
3824        return f"WHEN {matched}{source}{condition} THEN {then}"
def whens_sql(self, expression: sqlglot.expressions.Whens) -> str:
3826    def whens_sql(self, expression: exp.Whens) -> str:
3827        return self.expressions(expression, sep=" ", indent=False)
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
3829    def merge_sql(self, expression: exp.Merge) -> str:
3830        table = expression.this
3831        table_alias = ""
3832
3833        hints = table.args.get("hints")
3834        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3835            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3836            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3837
3838        this = self.sql(table)
3839        using = f"USING {self.sql(expression, 'using')}"
3840        on = f"ON {self.sql(expression, 'on')}"
3841        whens = self.sql(expression, "whens")
3842
3843        returning = self.sql(expression, "returning")
3844        if returning:
3845            whens = f"{whens}{returning}"
3846
3847        sep = self.sep()
3848
3849        return self.prepend_ctes(
3850            expression,
3851            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
3852        )
@unsupported_args('format')
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
3854    @unsupported_args("format")
3855    def tochar_sql(self, expression: exp.ToChar) -> str:
3856        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
def tonumber_sql(self, expression: sqlglot.expressions.ToNumber) -> str:
3858    def tonumber_sql(self, expression: exp.ToNumber) -> str:
3859        if not self.SUPPORTS_TO_NUMBER:
3860            self.unsupported("Unsupported TO_NUMBER function")
3861            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
3862
3863        fmt = expression.args.get("format")
3864        if not fmt:
3865            self.unsupported("Conversion format is required for TO_NUMBER")
3866            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
3867
3868        return self.func("TO_NUMBER", expression.this, fmt)
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
3870    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3871        this = self.sql(expression, "this")
3872        kind = self.sql(expression, "kind")
3873        settings_sql = self.expressions(expression, key="settings", sep=" ")
3874        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3875        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
3877    def dictrange_sql(self, expression: exp.DictRange) -> str:
3878        this = self.sql(expression, "this")
3879        max = self.sql(expression, "max")
3880        min = self.sql(expression, "min")
3881        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
3883    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3884        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def duplicatekeyproperty_sql(self, expression: sqlglot.expressions.DuplicateKeyProperty) -> str:
3886    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
3887        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
def uniquekeyproperty_sql(self, expression: sqlglot.expressions.UniqueKeyProperty) -> str:
3890    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
3891        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
def distributedbyproperty_sql(self, expression: sqlglot.expressions.DistributedByProperty) -> str:
3894    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
3895        expressions = self.expressions(expression, flat=True)
3896        expressions = f" {self.wrap(expressions)}" if expressions else ""
3897        buckets = self.sql(expression, "buckets")
3898        kind = self.sql(expression, "kind")
3899        buckets = f" BUCKETS {buckets}" if buckets else ""
3900        order = self.sql(expression, "order")
3901        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
3903    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3904        return ""
def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str:
3906    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3907        expressions = self.expressions(expression, key="expressions", flat=True)
3908        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3909        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3910        buckets = self.sql(expression, "buckets")
3911        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str:
3913    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3914        this = self.sql(expression, "this")
3915        having = self.sql(expression, "having")
3916
3917        if having:
3918            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3919
3920        return self.func("ANY_VALUE", this)
def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str:
3922    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3923        transform = self.func("TRANSFORM", *expression.expressions)
3924        row_format_before = self.sql(expression, "row_format_before")
3925        row_format_before = f" {row_format_before}" if row_format_before else ""
3926        record_writer = self.sql(expression, "record_writer")
3927        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3928        using = f" USING {self.sql(expression, 'command_script')}"
3929        schema = self.sql(expression, "schema")
3930        schema = f" AS {schema}" if schema else ""
3931        row_format_after = self.sql(expression, "row_format_after")
3932        row_format_after = f" {row_format_after}" if row_format_after else ""
3933        record_reader = self.sql(expression, "record_reader")
3934        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3935        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str:
3937    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3938        key_block_size = self.sql(expression, "key_block_size")
3939        if key_block_size:
3940            return f"KEY_BLOCK_SIZE = {key_block_size}"
3941
3942        using = self.sql(expression, "using")
3943        if using:
3944            return f"USING {using}"
3945
3946        parser = self.sql(expression, "parser")
3947        if parser:
3948            return f"WITH PARSER {parser}"
3949
3950        comment = self.sql(expression, "comment")
3951        if comment:
3952            return f"COMMENT {comment}"
3953
3954        visible = expression.args.get("visible")
3955        if visible is not None:
3956            return "VISIBLE" if visible else "INVISIBLE"
3957
3958        engine_attr = self.sql(expression, "engine_attr")
3959        if engine_attr:
3960            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3961
3962        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3963        if secondary_engine_attr:
3964            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3965
3966        self.unsupported("Unsupported index constraint option.")
3967        return ""
def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str:
3969    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3970        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3971        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str:
3973    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3974        kind = self.sql(expression, "kind")
3975        kind = f"{kind} INDEX" if kind else "INDEX"
3976        this = self.sql(expression, "this")
3977        this = f" {this}" if this else ""
3978        index_type = self.sql(expression, "index_type")
3979        index_type = f" USING {index_type}" if index_type else ""
3980        expressions = self.expressions(expression, flat=True)
3981        expressions = f" ({expressions})" if expressions else ""
3982        options = self.expressions(expression, key="options", sep=" ")
3983        options = f" {options}" if options else ""
3984        return f"{kind}{this}{index_type}{expressions}{options}"
def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str:
3986    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3987        if self.NVL2_SUPPORTED:
3988            return self.function_fallback_sql(expression)
3989
3990        case = exp.Case().when(
3991            expression.this.is_(exp.null()).not_(copy=False),
3992            expression.args["true"],
3993            copy=False,
3994        )
3995        else_cond = expression.args.get("false")
3996        if else_cond:
3997            case.else_(else_cond, copy=False)
3998
3999        return self.sql(case)
def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str:
4001    def comprehension_sql(self, expression: exp.Comprehension) -> str:
4002        this = self.sql(expression, "this")
4003        expr = self.sql(expression, "expression")
4004        iterator = self.sql(expression, "iterator")
4005        condition = self.sql(expression, "condition")
4006        condition = f" IF {condition}" if condition else ""
4007        return f"{this} FOR {expr} IN {iterator}{condition}"
def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str:
4009    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
4010        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str:
4012    def opclass_sql(self, expression: exp.Opclass) -> str:
4013        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def predict_sql(self, expression: sqlglot.expressions.Predict) -> str:
4015    def predict_sql(self, expression: exp.Predict) -> str:
4016        model = self.sql(expression, "this")
4017        model = f"MODEL {model}"
4018        table = self.sql(expression, "expression")
4019        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
4020        parameters = self.sql(expression, "params_struct")
4021        return self.func("PREDICT", model, table, parameters or None)
def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str:
4023    def forin_sql(self, expression: exp.ForIn) -> str:
4024        this = self.sql(expression, "this")
4025        expression_sql = self.sql(expression, "expression")
4026        return f"FOR {this} DO {expression_sql}"
def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str:
4028    def refresh_sql(self, expression: exp.Refresh) -> str:
4029        this = self.sql(expression, "this")
4030        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
4031        return f"REFRESH {table}{this}"
def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str:
4033    def toarray_sql(self, expression: exp.ToArray) -> str:
4034        arg = expression.this
4035        if not arg.type:
4036            from sqlglot.optimizer.annotate_types import annotate_types
4037
4038            arg = annotate_types(arg, dialect=self.dialect)
4039
4040        if arg.is_type(exp.DataType.Type.ARRAY):
4041            return self.sql(arg)
4042
4043        cond_for_null = arg.is_(exp.null())
4044        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
def tsordstotime_sql(self, expression: sqlglot.expressions.TsOrDsToTime) -> str:
4046    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
4047        this = expression.this
4048        time_format = self.format_time(expression)
4049
4050        if time_format:
4051            return self.sql(
4052                exp.cast(
4053                    exp.StrToTime(this=this, format=expression.args["format"]),
4054                    exp.DataType.Type.TIME,
4055                )
4056            )
4057
4058        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
4059            return self.sql(this)
4060
4061        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
def tsordstotimestamp_sql(self, expression: sqlglot.expressions.TsOrDsToTimestamp) -> str:
4063    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
4064        this = expression.this
4065        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
4066            return self.sql(this)
4067
4068        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
def tsordstodatetime_sql(self, expression: sqlglot.expressions.TsOrDsToDatetime) -> str:
4070    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
4071        this = expression.this
4072        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
4073            return self.sql(this)
4074
4075        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str:
4077    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
4078        this = expression.this
4079        time_format = self.format_time(expression)
4080
4081        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
4082            return self.sql(
4083                exp.cast(
4084                    exp.StrToTime(this=this, format=expression.args["format"]),
4085                    exp.DataType.Type.DATE,
4086                )
4087            )
4088
4089        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
4090            return self.sql(this)
4091
4092        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str:
4094    def unixdate_sql(self, expression: exp.UnixDate) -> str:
4095        return self.sql(
4096            exp.func(
4097                "DATEDIFF",
4098                expression.this,
4099                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
4100                "day",
4101            )
4102        )
def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str:
4104    def lastday_sql(self, expression: exp.LastDay) -> str:
4105        if self.LAST_DAY_SUPPORTS_DATE_PART:
4106            return self.function_fallback_sql(expression)
4107
4108        unit = expression.text("unit")
4109        if unit and unit != "MONTH":
4110            self.unsupported("Date parts are not supported in LAST_DAY.")
4111
4112        return self.func("LAST_DAY", expression.this)
def dateadd_sql(self, expression: sqlglot.expressions.DateAdd) -> str:
4114    def dateadd_sql(self, expression: exp.DateAdd) -> str:
4115        from sqlglot.dialects.dialect import unit_to_str
4116
4117        return self.func(
4118            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
4119        )
def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str:
4121    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
4122        if self.CAN_IMPLEMENT_ARRAY_ANY:
4123            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
4124            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
4125            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
4126            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
4127
4128        from sqlglot.dialects import Dialect
4129
4130        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
4131        if self.dialect.__class__ != Dialect:
4132            self.unsupported("ARRAY_ANY is unsupported")
4133
4134        return self.function_fallback_sql(expression)
def struct_sql(self, expression: sqlglot.expressions.Struct) -> str:
4136    def struct_sql(self, expression: exp.Struct) -> str:
4137        expression.set(
4138            "expressions",
4139            [
4140                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
4141                if isinstance(e, exp.PropertyEQ)
4142                else e
4143                for e in expression.expressions
4144            ],
4145        )
4146
4147        return self.function_fallback_sql(expression)
def partitionrange_sql(self, expression: sqlglot.expressions.PartitionRange) -> str:
4149    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
4150        low = self.sql(expression, "this")
4151        high = self.sql(expression, "expression")
4152
4153        return f"{low} TO {high}"
def truncatetable_sql(self, expression: sqlglot.expressions.TruncateTable) -> str:
4155    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
4156        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
4157        tables = f" {self.expressions(expression)}"
4158
4159        exists = " IF EXISTS" if expression.args.get("exists") else ""
4160
4161        on_cluster = self.sql(expression, "cluster")
4162        on_cluster = f" {on_cluster}" if on_cluster else ""
4163
4164        identity = self.sql(expression, "identity")
4165        identity = f" {identity} IDENTITY" if identity else ""
4166
4167        option = self.sql(expression, "option")
4168        option = f" {option}" if option else ""
4169
4170        partition = self.sql(expression, "partition")
4171        partition = f" {partition}" if partition else ""
4172
4173        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
def convert_sql(self, expression: sqlglot.expressions.Convert) -> str:
4177    def convert_sql(self, expression: exp.Convert) -> str:
4178        to = expression.this
4179        value = expression.expression
4180        style = expression.args.get("style")
4181        safe = expression.args.get("safe")
4182        strict = expression.args.get("strict")
4183
4184        if not to or not value:
4185            return ""
4186
4187        # Retrieve length of datatype and override to default if not specified
4188        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4189            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
4190
4191        transformed: t.Optional[exp.Expression] = None
4192        cast = exp.Cast if strict else exp.TryCast
4193
4194        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
4195        if isinstance(style, exp.Literal) and style.is_int:
4196            from sqlglot.dialects.tsql import TSQL
4197
4198            style_value = style.name
4199            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
4200            if not converted_style:
4201                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
4202
4203            fmt = exp.Literal.string(converted_style)
4204
4205            if to.this == exp.DataType.Type.DATE:
4206                transformed = exp.StrToDate(this=value, format=fmt)
4207            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
4208                transformed = exp.StrToTime(this=value, format=fmt)
4209            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
4210                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
4211            elif to.this == exp.DataType.Type.TEXT:
4212                transformed = exp.TimeToStr(this=value, format=fmt)
4213
4214        if not transformed:
4215            transformed = cast(this=value, to=to, safe=safe)
4216
4217        return self.sql(transformed)
def copyparameter_sql(self, expression: sqlglot.expressions.CopyParameter) -> str:
4277    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
4278        option = self.sql(expression, "this")
4279
4280        if expression.expressions:
4281            upper = option.upper()
4282
4283            # Snowflake FILE_FORMAT options are separated by whitespace
4284            sep = " " if upper == "FILE_FORMAT" else ", "
4285
4286            # Databricks copy/format options do not set their list of values with EQ
4287            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
4288            values = self.expressions(expression, flat=True, sep=sep)
4289            return f"{option}{op}({values})"
4290
4291        value = self.sql(expression, "expression")
4292
4293        if not value:
4294            return option
4295
4296        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
4297
4298        return f"{option}{op}{value}"
def credentials_sql(self, expression: sqlglot.expressions.Credentials) -> str:
4300    def credentials_sql(self, expression: exp.Credentials) -> str:
4301        cred_expr = expression.args.get("credentials")
4302        if isinstance(cred_expr, exp.Literal):
4303            # Redshift case: CREDENTIALS <string>
4304            credentials = self.sql(expression, "credentials")
4305            credentials = f"CREDENTIALS {credentials}" if credentials else ""
4306        else:
4307            # Snowflake case: CREDENTIALS = (...)
4308            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
4309            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
4310
4311        storage = self.sql(expression, "storage")
4312        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
4313
4314        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
4315        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
4316
4317        iam_role = self.sql(expression, "iam_role")
4318        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
4319
4320        region = self.sql(expression, "region")
4321        region = f" REGION {region}" if region else ""
4322
4323        return f"{credentials}{storage}{encryption}{iam_role}{region}"
def copy_sql(self, expression: sqlglot.expressions.Copy) -> str:
4325    def copy_sql(self, expression: exp.Copy) -> str:
4326        this = self.sql(expression, "this")
4327        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
4328
4329        credentials = self.sql(expression, "credentials")
4330        credentials = self.seg(credentials) if credentials else ""
4331        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
4332        files = self.expressions(expression, key="files", flat=True)
4333
4334        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
4335        params = self.expressions(
4336            expression,
4337            key="params",
4338            sep=sep,
4339            new_line=True,
4340            skip_last=True,
4341            skip_first=True,
4342            indent=self.COPY_PARAMS_ARE_WRAPPED,
4343        )
4344
4345        if params:
4346            if self.COPY_PARAMS_ARE_WRAPPED:
4347                params = f" WITH ({params})"
4348            elif not self.pretty:
4349                params = f" {params}"
4350
4351        return f"COPY{this}{kind} {files}{credentials}{params}"
def semicolon_sql(self, expression: sqlglot.expressions.Semicolon) -> str:
4353    def semicolon_sql(self, expression: exp.Semicolon) -> str:
4354        return ""
def datadeletionproperty_sql(self, expression: sqlglot.expressions.DataDeletionProperty) -> str:
4356    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
4357        on_sql = "ON" if expression.args.get("on") else "OFF"
4358        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
4359        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
4360        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
4361        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
4362
4363        if filter_col or retention_period:
4364            on_sql = self.func("ON", filter_col, retention_period)
4365
4366        return f"DATA_DELETION={on_sql}"
def maskingpolicycolumnconstraint_sql( self, expression: sqlglot.expressions.MaskingPolicyColumnConstraint) -> str:
4368    def maskingpolicycolumnconstraint_sql(
4369        self, expression: exp.MaskingPolicyColumnConstraint
4370    ) -> str:
4371        this = self.sql(expression, "this")
4372        expressions = self.expressions(expression, flat=True)
4373        expressions = f" USING ({expressions})" if expressions else ""
4374        return f"MASKING POLICY {this}{expressions}"
def gapfill_sql(self, expression: sqlglot.expressions.GapFill) -> str:
4376    def gapfill_sql(self, expression: exp.GapFill) -> str:
4377        this = self.sql(expression, "this")
4378        this = f"TABLE {this}"
4379        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
def scope_resolution(self, rhs: str, scope_name: str) -> str:
4381    def scope_resolution(self, rhs: str, scope_name: str) -> str:
4382        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
def scoperesolution_sql(self, expression: sqlglot.expressions.ScopeResolution) -> str:
4384    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
4385        this = self.sql(expression, "this")
4386        expr = expression.expression
4387
4388        if isinstance(expr, exp.Func):
4389            # T-SQL's CLR functions are case sensitive
4390            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
4391        else:
4392            expr = self.sql(expression, "expression")
4393
4394        return self.scope_resolution(expr, this)
def parsejson_sql(self, expression: sqlglot.expressions.ParseJSON) -> str:
4396    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
4397        if self.PARSE_JSON_NAME is None:
4398            return self.sql(expression.this)
4399
4400        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
def rand_sql(self, expression: sqlglot.expressions.Rand) -> str:
4402    def rand_sql(self, expression: exp.Rand) -> str:
4403        lower = self.sql(expression, "lower")
4404        upper = self.sql(expression, "upper")
4405
4406        if lower and upper:
4407            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
4408        return self.func("RAND", expression.this)
def changes_sql(self, expression: sqlglot.expressions.Changes) -> str:
4410    def changes_sql(self, expression: exp.Changes) -> str:
4411        information = self.sql(expression, "information")
4412        information = f"INFORMATION => {information}"
4413        at_before = self.sql(expression, "at_before")
4414        at_before = f"{self.seg('')}{at_before}" if at_before else ""
4415        end = self.sql(expression, "end")
4416        end = f"{self.seg('')}{end}" if end else ""
4417
4418        return f"CHANGES ({information}){at_before}{end}"
def pad_sql(self, expression: sqlglot.expressions.Pad) -> str:
4420    def pad_sql(self, expression: exp.Pad) -> str:
4421        prefix = "L" if expression.args.get("is_left") else "R"
4422
4423        fill_pattern = self.sql(expression, "fill_pattern") or None
4424        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
4425            fill_pattern = "' '"
4426
4427        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
def summarize_sql(self, expression: sqlglot.expressions.Summarize) -> str:
4429    def summarize_sql(self, expression: exp.Summarize) -> str:
4430        table = " TABLE" if expression.args.get("table") else ""
4431        return f"SUMMARIZE{table} {self.sql(expression.this)}"
def explodinggenerateseries_sql(self, expression: sqlglot.expressions.ExplodingGenerateSeries) -> str:
4433    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
4434        generate_series = exp.GenerateSeries(**expression.args)
4435
4436        parent = expression.parent
4437        if isinstance(parent, (exp.Alias, exp.TableAlias)):
4438            parent = parent.parent
4439
4440        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
4441            return self.sql(exp.Unnest(expressions=[generate_series]))
4442
4443        if isinstance(parent, exp.Select):
4444            self.unsupported("GenerateSeries projection unnesting is not supported.")
4445
4446        return self.sql(generate_series)
def arrayconcat_sql( self, expression: sqlglot.expressions.ArrayConcat, name: str = 'ARRAY_CONCAT') -> str:
4448    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
4449        exprs = expression.expressions
4450        if not self.ARRAY_CONCAT_IS_VAR_LEN:
4451            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
4452        else:
4453            rhs = self.expressions(expression)
4454
4455        return self.func(name, expression.this, rhs or None)
def converttimezone_sql(self, expression: sqlglot.expressions.ConvertTimezone) -> str:
4457    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
4458        if self.SUPPORTS_CONVERT_TIMEZONE:
4459            return self.function_fallback_sql(expression)
4460
4461        source_tz = expression.args.get("source_tz")
4462        target_tz = expression.args.get("target_tz")
4463        timestamp = expression.args.get("timestamp")
4464
4465        if source_tz and timestamp:
4466            timestamp = exp.AtTimeZone(
4467                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
4468            )
4469
4470        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
4471
4472        return self.sql(expr)
def json_sql(self, expression: sqlglot.expressions.JSON) -> str:
4474    def json_sql(self, expression: exp.JSON) -> str:
4475        this = self.sql(expression, "this")
4476        this = f" {this}" if this else ""
4477
4478        _with = expression.args.get("with")
4479
4480        if _with is None:
4481            with_sql = ""
4482        elif not _with:
4483            with_sql = " WITHOUT"
4484        else:
4485            with_sql = " WITH"
4486
4487        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
4488
4489        return f"JSON{this}{with_sql}{unique_sql}"
def jsonvalue_sql(self, expression: sqlglot.expressions.JSONValue) -> str:
4491    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
4492        def _generate_on_options(arg: t.Any) -> str:
4493            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
4494
4495        path = self.sql(expression, "path")
4496        returning = self.sql(expression, "returning")
4497        returning = f" RETURNING {returning}" if returning else ""
4498
4499        on_condition = self.sql(expression, "on_condition")
4500        on_condition = f" {on_condition}" if on_condition else ""
4501
4502        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
def conditionalinsert_sql(self, expression: sqlglot.expressions.ConditionalInsert) -> str:
4504    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
4505        else_ = "ELSE " if expression.args.get("else_") else ""
4506        condition = self.sql(expression, "expression")
4507        condition = f"WHEN {condition} THEN " if condition else else_
4508        insert = self.sql(expression, "this")[len("INSERT") :].strip()
4509        return f"{condition}{insert}"
def multitableinserts_sql(self, expression: sqlglot.expressions.MultitableInserts) -> str:
4511    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
4512        kind = self.sql(expression, "kind")
4513        expressions = self.seg(self.expressions(expression, sep=" "))
4514        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
4515        return res
def oncondition_sql(self, expression: sqlglot.expressions.OnCondition) -> str:
4517    def oncondition_sql(self, expression: exp.OnCondition) -> str:
4518        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
4519        empty = expression.args.get("empty")
4520        empty = (
4521            f"DEFAULT {empty} ON EMPTY"
4522            if isinstance(empty, exp.Expression)
4523            else self.sql(expression, "empty")
4524        )
4525
4526        error = expression.args.get("error")
4527        error = (
4528            f"DEFAULT {error} ON ERROR"
4529            if isinstance(error, exp.Expression)
4530            else self.sql(expression, "error")
4531        )
4532
4533        if error and empty:
4534            error = (
4535                f"{empty} {error}"
4536                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
4537                else f"{error} {empty}"
4538            )
4539            empty = ""
4540
4541        null = self.sql(expression, "null")
4542
4543        return f"{empty}{error}{null}"
def jsonextractquote_sql(self, expression: sqlglot.expressions.JSONExtractQuote) -> str:
4545    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
4546        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
4547        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
def jsonexists_sql(self, expression: sqlglot.expressions.JSONExists) -> str:
4549    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
4550        this = self.sql(expression, "this")
4551        path = self.sql(expression, "path")
4552
4553        passing = self.expressions(expression, "passing")
4554        passing = f" PASSING {passing}" if passing else ""
4555
4556        on_condition = self.sql(expression, "on_condition")
4557        on_condition = f" {on_condition}" if on_condition else ""
4558
4559        path = f"{path}{passing}{on_condition}"
4560
4561        return self.func("JSON_EXISTS", this, path)
def arrayagg_sql(self, expression: sqlglot.expressions.ArrayAgg) -> str:
4563    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
4564        array_agg = self.function_fallback_sql(expression)
4565
4566        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
4567        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
4568        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
4569            parent = expression.parent
4570            if isinstance(parent, exp.Filter):
4571                parent_cond = parent.expression.this
4572                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
4573            else:
4574                this = expression.this
4575                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
4576                if this.find(exp.Column):
4577                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
4578                    this_sql = (
4579                        self.expressions(this)
4580                        if isinstance(this, exp.Distinct)
4581                        else self.sql(expression, "this")
4582                    )
4583
4584                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
4585
4586        return array_agg
def apply_sql(self, expression: sqlglot.expressions.Apply) -> str:
4588    def apply_sql(self, expression: exp.Apply) -> str:
4589        this = self.sql(expression, "this")
4590        expr = self.sql(expression, "expression")
4591
4592        return f"{this} APPLY({expr})"
def grant_sql(self, expression: sqlglot.expressions.Grant) -> str:
4594    def grant_sql(self, expression: exp.Grant) -> str:
4595        privileges_sql = self.expressions(expression, key="privileges", flat=True)
4596
4597        kind = self.sql(expression, "kind")
4598        kind = f" {kind}" if kind else ""
4599
4600        securable = self.sql(expression, "securable")
4601        securable = f" {securable}" if securable else ""
4602
4603        principals = self.expressions(expression, key="principals", flat=True)
4604
4605        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
4606
4607        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
def grantprivilege_sql(self, expression: sqlglot.expressions.GrantPrivilege):
4609    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
4610        this = self.sql(expression, "this")
4611        columns = self.expressions(expression, flat=True)
4612        columns = f"({columns})" if columns else ""
4613
4614        return f"{this}{columns}"
def grantprincipal_sql(self, expression: sqlglot.expressions.GrantPrincipal):
4616    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
4617        this = self.sql(expression, "this")
4618
4619        kind = self.sql(expression, "kind")
4620        kind = f"{kind} " if kind else ""
4621
4622        return f"{kind}{this}"
def columns_sql(self, expression: sqlglot.expressions.Columns):
4624    def columns_sql(self, expression: exp.Columns):
4625        func = self.function_fallback_sql(expression)
4626        if expression.args.get("unpack"):
4627            func = f"*{func}"
4628
4629        return func
def overlay_sql(self, expression: sqlglot.expressions.Overlay):
4631    def overlay_sql(self, expression: exp.Overlay):
4632        this = self.sql(expression, "this")
4633        expr = self.sql(expression, "expression")
4634        from_sql = self.sql(expression, "from")
4635        for_sql = self.sql(expression, "for")
4636        for_sql = f" FOR {for_sql}" if for_sql else ""
4637
4638        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
@unsupported_args('format')
def todouble_sql(self, expression: sqlglot.expressions.ToDouble) -> str:
4640    @unsupported_args("format")
4641    def todouble_sql(self, expression: exp.ToDouble) -> str:
4642        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
def string_sql(self, expression: sqlglot.expressions.String) -> str:
4644    def string_sql(self, expression: exp.String) -> str:
4645        this = expression.this
4646        zone = expression.args.get("zone")
4647
4648        if zone:
4649            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
4650            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
4651            # set for source_tz to transpile the time conversion before the STRING cast
4652            this = exp.ConvertTimezone(
4653                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
4654            )
4655
4656        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
def median_sql(self, expression: sqlglot.expressions.Median):
4658    def median_sql(self, expression: exp.Median):
4659        if not self.SUPPORTS_MEDIAN:
4660            return self.sql(
4661                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
4662            )
4663
4664        return self.function_fallback_sql(expression)
def overflowtruncatebehavior_sql(self, expression: sqlglot.expressions.OverflowTruncateBehavior) -> str:
4666    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
4667        filler = self.sql(expression, "this")
4668        filler = f" {filler}" if filler else ""
4669        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
4670        return f"TRUNCATE{filler} {with_count}"
def unixseconds_sql(self, expression: sqlglot.expressions.UnixSeconds) -> str:
4672    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
4673        if self.SUPPORTS_UNIX_SECONDS:
4674            return self.function_fallback_sql(expression)
4675
4676        start_ts = exp.cast(
4677            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
4678        )
4679
4680        return self.sql(
4681            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
4682        )
def arraysize_sql(self, expression: sqlglot.expressions.ArraySize) -> str:
4684    def arraysize_sql(self, expression: exp.ArraySize) -> str:
4685        dim = expression.expression
4686
4687        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
4688        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
4689            if not (dim.is_int and dim.name == "1"):
4690                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
4691            dim = None
4692
4693        # If dimension is required but not specified, default initialize it
4694        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
4695            dim = exp.Literal.number(1)
4696
4697        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
def attach_sql(self, expression: sqlglot.expressions.Attach) -> str:
4699    def attach_sql(self, expression: exp.Attach) -> str:
4700        this = self.sql(expression, "this")
4701        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
4702        expressions = self.expressions(expression)
4703        expressions = f" ({expressions})" if expressions else ""
4704
4705        return f"ATTACH{exists_sql} {this}{expressions}"
def detach_sql(self, expression: sqlglot.expressions.Detach) -> str:
4707    def detach_sql(self, expression: exp.Detach) -> str:
4708        this = self.sql(expression, "this")
4709        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
4710
4711        return f"DETACH{exists_sql} {this}"
def attachoption_sql(self, expression: sqlglot.expressions.AttachOption) -> str:
4713    def attachoption_sql(self, expression: exp.AttachOption) -> str:
4714        this = self.sql(expression, "this")
4715        value = self.sql(expression, "expression")
4716        value = f" {value}" if value else ""
4717        return f"{this}{value}"
def featuresattime_sql(self, expression: sqlglot.expressions.FeaturesAtTime) -> str:
4719    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
4720        this_sql = self.sql(expression, "this")
4721        if isinstance(expression.this, exp.Table):
4722            this_sql = f"TABLE {this_sql}"
4723
4724        return self.func(
4725            "FEATURES_AT_TIME",
4726            this_sql,
4727            expression.args.get("time"),
4728            expression.args.get("num_rows"),
4729            expression.args.get("ignore_feature_nulls"),
4730        )
def watermarkcolumnconstraint_sql(self, expression: sqlglot.expressions.WatermarkColumnConstraint) -> str:
4732    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
4733        return (
4734            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
4735        )
def encodeproperty_sql(self, expression: sqlglot.expressions.EncodeProperty) -> str:
4737    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
4738        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
4739        encode = f"{encode} {self.sql(expression, 'this')}"
4740
4741        properties = expression.args.get("properties")
4742        if properties:
4743            encode = f"{encode} {self.properties(properties)}"
4744
4745        return encode
def includeproperty_sql(self, expression: sqlglot.expressions.IncludeProperty) -> str:
4747    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
4748        this = self.sql(expression, "this")
4749        include = f"INCLUDE {this}"
4750
4751        column_def = self.sql(expression, "column_def")
4752        if column_def:
4753            include = f"{include} {column_def}"
4754
4755        alias = self.sql(expression, "alias")
4756        if alias:
4757            include = f"{include} AS {alias}"
4758
4759        return include
def xmlelement_sql(self, expression: sqlglot.expressions.XMLElement) -> str:
4761    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
4762        name = f"NAME {self.sql(expression, 'this')}"
4763        return self.func("XMLELEMENT", name, *expression.expressions)
def partitionbyrangeproperty_sql(self, expression: sqlglot.expressions.PartitionByRangeProperty) -> str:
4765    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
4766        partitions = self.expressions(expression, "partition_expressions")
4767        create = self.expressions(expression, "create_expressions")
4768        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
def partitionbyrangepropertydynamic_sql( self, expression: sqlglot.expressions.PartitionByRangePropertyDynamic) -> str:
4770    def partitionbyrangepropertydynamic_sql(
4771        self, expression: exp.PartitionByRangePropertyDynamic
4772    ) -> str:
4773        start = self.sql(expression, "start")
4774        end = self.sql(expression, "end")
4775
4776        every = expression.args["every"]
4777        if isinstance(every, exp.Interval) and every.this.is_string:
4778            every.this.replace(exp.Literal.number(every.name))
4779
4780        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
def unpivotcolumns_sql(self, expression: sqlglot.expressions.UnpivotColumns) -> str:
4782    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
4783        name = self.sql(expression, "this")
4784        values = self.expressions(expression, flat=True)
4785
4786        return f"NAME {name} VALUE {values}"
def analyzesample_sql(self, expression: sqlglot.expressions.AnalyzeSample) -> str:
4788    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
4789        kind = self.sql(expression, "kind")
4790        sample = self.sql(expression, "sample")
4791        return f"SAMPLE {sample} {kind}"
def analyzestatistics_sql(self, expression: sqlglot.expressions.AnalyzeStatistics) -> str:
4793    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
4794        kind = self.sql(expression, "kind")
4795        option = self.sql(expression, "option")
4796        option = f" {option}" if option else ""
4797        this = self.sql(expression, "this")
4798        this = f" {this}" if this else ""
4799        columns = self.expressions(expression)
4800        columns = f" {columns}" if columns else ""
4801        return f"{kind}{option} STATISTICS{this}{columns}"
def analyzehistogram_sql(self, expression: sqlglot.expressions.AnalyzeHistogram) -> str:
4803    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
4804        this = self.sql(expression, "this")
4805        columns = self.expressions(expression)
4806        inner_expression = self.sql(expression, "expression")
4807        inner_expression = f" {inner_expression}" if inner_expression else ""
4808        update_options = self.sql(expression, "update_options")
4809        update_options = f" {update_options} UPDATE" if update_options else ""
4810        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
def analyzedelete_sql(self, expression: sqlglot.expressions.AnalyzeDelete) -> str:
4812    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
4813        kind = self.sql(expression, "kind")
4814        kind = f" {kind}" if kind else ""
4815        return f"DELETE{kind} STATISTICS"
def analyzelistchainedrows_sql(self, expression: sqlglot.expressions.AnalyzeListChainedRows) -> str:
4817    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
4818        inner_expression = self.sql(expression, "expression")
4819        return f"LIST CHAINED ROWS{inner_expression}"
def analyzevalidate_sql(self, expression: sqlglot.expressions.AnalyzeValidate) -> str:
4821    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
4822        kind = self.sql(expression, "kind")
4823        this = self.sql(expression, "this")
4824        this = f" {this}" if this else ""
4825        inner_expression = self.sql(expression, "expression")
4826        return f"VALIDATE {kind}{this}{inner_expression}"
def analyze_sql(self, expression: sqlglot.expressions.Analyze) -> str:
4828    def analyze_sql(self, expression: exp.Analyze) -> str:
4829        options = self.expressions(expression, key="options", sep=" ")
4830        options = f" {options}" if options else ""
4831        kind = self.sql(expression, "kind")
4832        kind = f" {kind}" if kind else ""
4833        this = self.sql(expression, "this")
4834        this = f" {this}" if this else ""
4835        mode = self.sql(expression, "mode")
4836        mode = f" {mode}" if mode else ""
4837        properties = self.sql(expression, "properties")
4838        properties = f" {properties}" if properties else ""
4839        partition = self.sql(expression, "partition")
4840        partition = f" {partition}" if partition else ""
4841        inner_expression = self.sql(expression, "expression")
4842        inner_expression = f" {inner_expression}" if inner_expression else ""
4843        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
def xmltable_sql(self, expression: sqlglot.expressions.XMLTable) -> str:
4845    def xmltable_sql(self, expression: exp.XMLTable) -> str:
4846        this = self.sql(expression, "this")
4847        namespaces = self.expressions(expression, key="namespaces")
4848        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
4849        passing = self.expressions(expression, key="passing")
4850        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
4851        columns = self.expressions(expression, key="columns")
4852        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
4853        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
4854        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
def xmlnamespace_sql(self, expression: sqlglot.expressions.XMLNamespace) -> str:
4856    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
4857        this = self.sql(expression, "this")
4858        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
def export_sql(self, expression: sqlglot.expressions.Export) -> str:
4860    def export_sql(self, expression: exp.Export) -> str:
4861        this = self.sql(expression, "this")
4862        connection = self.sql(expression, "connection")
4863        connection = f"WITH CONNECTION {connection} " if connection else ""
4864        options = self.sql(expression, "options")
4865        return f"EXPORT DATA {connection}{options} AS {this}"
def declare_sql(self, expression: sqlglot.expressions.Declare) -> str:
4867    def declare_sql(self, expression: exp.Declare) -> str:
4868        return f"DECLARE {self.expressions(expression, flat=True)}"
def declareitem_sql(self, expression: sqlglot.expressions.DeclareItem) -> str:
4870    def declareitem_sql(self, expression: exp.DeclareItem) -> str:
4871        variable = self.sql(expression, "this")
4872        default = self.sql(expression, "default")
4873        default = f" = {default}" if default else ""
4874
4875        kind = self.sql(expression, "kind")
4876        if isinstance(expression.args.get("kind"), exp.Schema):
4877            kind = f"TABLE {kind}"
4878
4879        return f"{variable} AS {kind}{default}"
def recursivewithsearch_sql(self, expression: sqlglot.expressions.RecursiveWithSearch) -> str:
4881    def recursivewithsearch_sql(self, expression: exp.RecursiveWithSearch) -> str:
4882        kind = self.sql(expression, "kind")
4883        this = self.sql(expression, "this")
4884        set = self.sql(expression, "expression")
4885        using = self.sql(expression, "using")
4886        using = f" USING {using}" if using else ""
4887
4888        kind_sql = kind if kind == "CYCLE" else f"SEARCH {kind} FIRST BY"
4889
4890        return f"{kind_sql} {this} SET {set}{using}"
def parameterizedagg_sql(self, expression: sqlglot.expressions.ParameterizedAgg) -> str:
4892    def parameterizedagg_sql(self, expression: exp.ParameterizedAgg) -> str:
4893        params = self.expressions(expression, key="params", flat=True)
4894        return self.func(expression.name, *expression.expressions) + f"({params})"
def anonymousaggfunc_sql(self, expression: sqlglot.expressions.AnonymousAggFunc) -> str:
4896    def anonymousaggfunc_sql(self, expression: exp.AnonymousAggFunc) -> str:
4897        return self.func(expression.name, *expression.expressions)
def combinedaggfunc_sql(self, expression: sqlglot.expressions.CombinedAggFunc) -> str:
4899    def combinedaggfunc_sql(self, expression: exp.CombinedAggFunc) -> str:
4900        return self.anonymousaggfunc_sql(expression)
def combinedparameterizedagg_sql(self, expression: sqlglot.expressions.CombinedParameterizedAgg) -> str:
4902    def combinedparameterizedagg_sql(self, expression: exp.CombinedParameterizedAgg) -> str:
4903        return self.parameterizedagg_sql(expression)
def show_sql(self, expression: sqlglot.expressions.Show) -> str:
4905    def show_sql(self, expression: exp.Show) -> str:
4906        self.unsupported("Unsupported SHOW statement")
4907        return ""
def get_put_sql( self, expression: sqlglot.expressions.Put | sqlglot.expressions.Get) -> str:
4909    def get_put_sql(self, expression: exp.Put | exp.Get) -> str:
4910        # Snowflake GET/PUT statements:
4911        #   PUT <file> <internalStage> <properties>
4912        #   GET <internalStage> <file> <properties>
4913        props = expression.args.get("properties")
4914        props_sql = self.properties(props, prefix=" ", sep=" ", wrapped=False) if props else ""
4915        this = self.sql(expression, "this")
4916        target = self.sql(expression, "target")
4917
4918        if isinstance(expression, exp.Put):
4919            return f"PUT {this} {target}{props_sql}"
4920        else:
4921            return f"GET {target} {this}{props_sql}"