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
   8
   9from sqlglot import exp
  10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
  11from sqlglot.helper import apply_index_offset, csv, 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
  20logger = logging.getLogger("sqlglot")
  21
  22ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
  23
  24
  25class _Generator(type):
  26    def __new__(cls, clsname, bases, attrs):
  27        klass = super().__new__(cls, clsname, bases, attrs)
  28
  29        # Remove transforms that correspond to unsupported JSONPathPart expressions
  30        for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS:
  31            klass.TRANSFORMS.pop(part, None)
  32
  33        return klass
  34
  35
  36class Generator(metaclass=_Generator):
  37    """
  38    Generator converts a given syntax tree to the corresponding SQL string.
  39
  40    Args:
  41        pretty: Whether to format the produced SQL string.
  42            Default: False.
  43        identify: Determines when an identifier should be quoted. Possible values are:
  44            False (default): Never quote, except in cases where it's mandatory by the dialect.
  45            True or 'always': Always quote.
  46            'safe': Only quote identifiers that are case insensitive.
  47        normalize: Whether to normalize identifiers to lowercase.
  48            Default: False.
  49        pad: The pad size in a formatted string.
  50            Default: 2.
  51        indent: The indentation size in a formatted string.
  52            Default: 2.
  53        normalize_functions: How to normalize function names. Possible values are:
  54            "upper" or True (default): Convert names to uppercase.
  55            "lower": Convert names to lowercase.
  56            False: Disables function name normalization.
  57        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
  58            Default ErrorLevel.WARN.
  59        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
  60            This is only relevant if unsupported_level is ErrorLevel.RAISE.
  61            Default: 3
  62        leading_comma: Whether the comma is leading or trailing in select expressions.
  63            This is only relevant when generating in pretty mode.
  64            Default: False
  65        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
  66            The default is on the smaller end because the length only represents a segment and not the true
  67            line length.
  68            Default: 80
  69        comments: Whether to preserve comments in the output SQL code.
  70            Default: True
  71    """
  72
  73    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
  74        **JSON_PATH_PART_TRANSFORMS,
  75        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
  76        exp.CaseSpecificColumnConstraint: lambda _,
  77        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
  78        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
  79        exp.CharacterSetProperty: lambda self,
  80        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
  81        exp.ClusteredColumnConstraint: lambda self,
  82        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
  83        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
  84        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
  85        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
  86        exp.DateAdd: lambda self, e: self.func(
  87            "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
  88        ),
  89        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
  90        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
  91        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
  92        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
  93        exp.ExternalProperty: lambda *_: "EXTERNAL",
  94        exp.GlobalProperty: lambda *_: "GLOBAL",
  95        exp.HeapProperty: lambda *_: "HEAP",
  96        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
  97        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
  98        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
  99        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
 100        exp.JSONExtract: lambda self, e: self.func(
 101            "JSON_EXTRACT", e.this, e.expression, *e.expressions
 102        ),
 103        exp.JSONExtractScalar: lambda self, e: self.func(
 104            "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions
 105        ),
 106        exp.LanguageProperty: lambda self, e: self.naked_property(e),
 107        exp.LocationProperty: lambda self, e: self.naked_property(e),
 108        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 109        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
 110        exp.NonClusteredColumnConstraint: lambda self,
 111        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 112        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
 113        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
 114        exp.OnCommitProperty: lambda _,
 115        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
 116        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
 117        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
 118        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
 119        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
 120        exp.RemoteWithConnectionModelProperty: lambda self,
 121        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
 122        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
 123        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
 124        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
 125        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
 126        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
 127        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
 128        exp.SqlReadWriteProperty: lambda _, e: e.name,
 129        exp.SqlSecurityProperty: lambda _,
 130        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 131        exp.StabilityProperty: lambda _, e: e.name,
 132        exp.TemporaryProperty: lambda *_: "TEMPORARY",
 133        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
 134        exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression),
 135        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
 136        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
 137        exp.TransientProperty: lambda *_: "TRANSIENT",
 138        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
 139        exp.UnloggedProperty: lambda *_: "UNLOGGED",
 140        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 141        exp.VolatileProperty: lambda *_: "VOLATILE",
 142        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
 143    }
 144
 145    # Whether null ordering is supported in order by
 146    # True: Full Support, None: No support, False: No support in window specifications
 147    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
 148
 149    # Whether ignore nulls is inside the agg or outside.
 150    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
 151    IGNORE_NULLS_IN_FUNC = False
 152
 153    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 154    LOCKING_READS_SUPPORTED = False
 155
 156    # Always do union distinct or union all
 157    EXPLICIT_UNION = False
 158
 159    # Wrap derived values in parens, usually standard but spark doesn't support it
 160    WRAP_DERIVED_VALUES = True
 161
 162    # Whether create function uses an AS before the RETURN
 163    CREATE_FUNCTION_RETURN_AS = True
 164
 165    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
 166    MATCHED_BY_SOURCE = True
 167
 168    # Whether the INTERVAL expression works only with values like '1 day'
 169    SINGLE_STRING_INTERVAL = False
 170
 171    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
 172    INTERVAL_ALLOWS_PLURAL_FORM = True
 173
 174    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
 175    LIMIT_FETCH = "ALL"
 176
 177    # Whether limit and fetch allows expresions or just limits
 178    LIMIT_ONLY_LITERALS = False
 179
 180    # Whether a table is allowed to be renamed with a db
 181    RENAME_TABLE_WITH_DB = True
 182
 183    # The separator for grouping sets and rollups
 184    GROUPINGS_SEP = ","
 185
 186    # The string used for creating an index on a table
 187    INDEX_ON = "ON"
 188
 189    # Whether join hints should be generated
 190    JOIN_HINTS = True
 191
 192    # Whether table hints should be generated
 193    TABLE_HINTS = True
 194
 195    # Whether query hints should be generated
 196    QUERY_HINTS = True
 197
 198    # What kind of separator to use for query hints
 199    QUERY_HINT_SEP = ", "
 200
 201    # Whether comparing against booleans (e.g. x IS TRUE) is supported
 202    IS_BOOL_ALLOWED = True
 203
 204    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
 205    DUPLICATE_KEY_UPDATE_WITH_SET = True
 206
 207    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
 208    LIMIT_IS_TOP = False
 209
 210    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
 211    RETURNING_END = True
 212
 213    # Whether to generate the (+) suffix for columns used in old-style join conditions
 214    COLUMN_JOIN_MARKS_SUPPORTED = False
 215
 216    # Whether to generate an unquoted value for EXTRACT's date part argument
 217    EXTRACT_ALLOWS_QUOTES = True
 218
 219    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
 220    TZ_TO_WITH_TIME_ZONE = False
 221
 222    # Whether the NVL2 function is supported
 223    NVL2_SUPPORTED = True
 224
 225    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
 226    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
 227
 228    # Whether VALUES statements can be used as derived tables.
 229    # MySQL 5 and Redshift do not allow this, so when False, it will convert
 230    # SELECT * VALUES into SELECT UNION
 231    VALUES_AS_TABLE = True
 232
 233    # Whether the word COLUMN is included when adding a column with ALTER TABLE
 234    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
 235
 236    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
 237    UNNEST_WITH_ORDINALITY = True
 238
 239    # Whether FILTER (WHERE cond) can be used for conditional aggregation
 240    AGGREGATE_FILTER_SUPPORTED = True
 241
 242    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
 243    SEMI_ANTI_JOIN_WITH_SIDE = True
 244
 245    # Whether to include the type of a computed column in the CREATE DDL
 246    COMPUTED_COLUMN_WITH_TYPE = True
 247
 248    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
 249    SUPPORTS_TABLE_COPY = True
 250
 251    # Whether parentheses are required around the table sample's expression
 252    TABLESAMPLE_REQUIRES_PARENS = True
 253
 254    # Whether a table sample clause's size needs to be followed by the ROWS keyword
 255    TABLESAMPLE_SIZE_IS_ROWS = True
 256
 257    # The keyword(s) to use when generating a sample clause
 258    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
 259
 260    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
 261    TABLESAMPLE_WITH_METHOD = True
 262
 263    # The keyword to use when specifying the seed of a sample clause
 264    TABLESAMPLE_SEED_KEYWORD = "SEED"
 265
 266    # Whether COLLATE is a function instead of a binary operator
 267    COLLATE_IS_FUNC = False
 268
 269    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
 270    DATA_TYPE_SPECIFIERS_ALLOWED = False
 271
 272    # Whether conditions require booleans WHERE x = 0 vs WHERE x
 273    ENSURE_BOOLS = False
 274
 275    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
 276    CTE_RECURSIVE_KEYWORD_REQUIRED = True
 277
 278    # Whether CONCAT requires >1 arguments
 279    SUPPORTS_SINGLE_ARG_CONCAT = True
 280
 281    # Whether LAST_DAY function supports a date part argument
 282    LAST_DAY_SUPPORTS_DATE_PART = True
 283
 284    # Whether named columns are allowed in table aliases
 285    SUPPORTS_TABLE_ALIAS_COLUMNS = True
 286
 287    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
 288    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
 289
 290    # What delimiter to use for separating JSON key/value pairs
 291    JSON_KEY_VALUE_PAIR_SEP = ":"
 292
 293    # INSERT OVERWRITE TABLE x override
 294    INSERT_OVERWRITE = " OVERWRITE TABLE"
 295
 296    # Whether the SELECT .. INTO syntax is used instead of CTAS
 297    SUPPORTS_SELECT_INTO = False
 298
 299    # Whether UNLOGGED tables can be created
 300    SUPPORTS_UNLOGGED_TABLES = False
 301
 302    # Whether the CREATE TABLE LIKE statement is supported
 303    SUPPORTS_CREATE_TABLE_LIKE = True
 304
 305    # Whether the LikeProperty needs to be specified inside of the schema clause
 306    LIKE_PROPERTY_INSIDE_SCHEMA = False
 307
 308    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
 309    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
 310    MULTI_ARG_DISTINCT = True
 311
 312    # Whether the JSON extraction operators expect a value of type JSON
 313    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
 314
 315    # Whether bracketed keys like ["foo"] are supported in JSON paths
 316    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
 317
 318    # Whether to escape keys using single quotes in JSON paths
 319    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
 320
 321    # The JSONPathPart expressions supported by this dialect
 322    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
 323
 324    # Whether any(f(x) for x in array) can be implemented by this dialect
 325    CAN_IMPLEMENT_ARRAY_ANY = False
 326
 327    TYPE_MAPPING = {
 328        exp.DataType.Type.NCHAR: "CHAR",
 329        exp.DataType.Type.NVARCHAR: "VARCHAR",
 330        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 331        exp.DataType.Type.LONGTEXT: "TEXT",
 332        exp.DataType.Type.TINYTEXT: "TEXT",
 333        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 334        exp.DataType.Type.LONGBLOB: "BLOB",
 335        exp.DataType.Type.TINYBLOB: "BLOB",
 336        exp.DataType.Type.INET: "INET",
 337    }
 338
 339    STAR_MAPPING = {
 340        "except": "EXCEPT",
 341        "replace": "REPLACE",
 342    }
 343
 344    TIME_PART_SINGULARS = {
 345        "MICROSECONDS": "MICROSECOND",
 346        "SECONDS": "SECOND",
 347        "MINUTES": "MINUTE",
 348        "HOURS": "HOUR",
 349        "DAYS": "DAY",
 350        "WEEKS": "WEEK",
 351        "MONTHS": "MONTH",
 352        "QUARTERS": "QUARTER",
 353        "YEARS": "YEAR",
 354    }
 355
 356    AFTER_HAVING_MODIFIER_TRANSFORMS = {
 357        "cluster": lambda self, e: self.sql(e, "cluster"),
 358        "distribute": lambda self, e: self.sql(e, "distribute"),
 359        "qualify": lambda self, e: self.sql(e, "qualify"),
 360        "sort": lambda self, e: self.sql(e, "sort"),
 361        "windows": lambda self, e: (
 362            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
 363            if e.args.get("windows")
 364            else ""
 365        ),
 366    }
 367
 368    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 369
 370    STRUCT_DELIMITER = ("<", ">")
 371
 372    PARAMETER_TOKEN = "@"
 373    NAMED_PLACEHOLDER_TOKEN = ":"
 374
 375    PROPERTIES_LOCATION = {
 376        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 377        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 378        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
 379        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 380        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 381        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 382        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 383        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
 384        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 385        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
 386        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 387        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 388        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
 389        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
 390        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 391        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 392        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 393        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 394        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 395        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 396        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 397        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 398        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
 399        exp.HeapProperty: exp.Properties.Location.POST_WITH,
 400        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
 401        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
 402        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 403        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 404        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 405        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 406        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 407        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
 408        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 409        exp.LogProperty: exp.Properties.Location.POST_NAME,
 410        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 411        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 412        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 413        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 414        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 415        exp.Order: exp.Properties.Location.POST_SCHEMA,
 416        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 417        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 418        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 419        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 420        exp.Property: exp.Properties.Location.POST_WITH,
 421        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 422        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 423        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 424        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 425        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 426        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 427        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 428        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 429        exp.Set: exp.Properties.Location.POST_SCHEMA,
 430        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 431        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 432        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 433        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
 434        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
 435        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 436        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 437        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 438        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 439        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 440        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 441        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 442        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 443        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 444        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
 445        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 446        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 447        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 448        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 449    }
 450
 451    # Keywords that can't be used as unquoted identifier names
 452    RESERVED_KEYWORDS: t.Set[str] = set()
 453
 454    # Expressions whose comments are separated from them for better formatting
 455    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 456        exp.Create,
 457        exp.Delete,
 458        exp.Drop,
 459        exp.From,
 460        exp.Insert,
 461        exp.Join,
 462        exp.Select,
 463        exp.Union,
 464        exp.Update,
 465        exp.Where,
 466        exp.With,
 467    )
 468
 469    # Expressions that should not have their comments generated in maybe_comment
 470    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 471        exp.Binary,
 472        exp.Union,
 473    )
 474
 475    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 476    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 477        exp.Column,
 478        exp.Literal,
 479        exp.Neg,
 480        exp.Paren,
 481    )
 482
 483    PARAMETERIZABLE_TEXT_TYPES = {
 484        exp.DataType.Type.NVARCHAR,
 485        exp.DataType.Type.VARCHAR,
 486        exp.DataType.Type.CHAR,
 487        exp.DataType.Type.NCHAR,
 488    }
 489
 490    # Expressions that need to have all CTEs under them bubbled up to them
 491    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 492
 493    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 494
 495    __slots__ = (
 496        "pretty",
 497        "identify",
 498        "normalize",
 499        "pad",
 500        "_indent",
 501        "normalize_functions",
 502        "unsupported_level",
 503        "max_unsupported",
 504        "leading_comma",
 505        "max_text_width",
 506        "comments",
 507        "dialect",
 508        "unsupported_messages",
 509        "_escaped_quote_end",
 510        "_escaped_identifier_end",
 511    )
 512
 513    def __init__(
 514        self,
 515        pretty: t.Optional[bool] = None,
 516        identify: str | bool = False,
 517        normalize: bool = False,
 518        pad: int = 2,
 519        indent: int = 2,
 520        normalize_functions: t.Optional[str | bool] = None,
 521        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 522        max_unsupported: int = 3,
 523        leading_comma: bool = False,
 524        max_text_width: int = 80,
 525        comments: bool = True,
 526        dialect: DialectType = None,
 527    ):
 528        import sqlglot
 529        from sqlglot.dialects import Dialect
 530
 531        self.pretty = pretty if pretty is not None else sqlglot.pretty
 532        self.identify = identify
 533        self.normalize = normalize
 534        self.pad = pad
 535        self._indent = indent
 536        self.unsupported_level = unsupported_level
 537        self.max_unsupported = max_unsupported
 538        self.leading_comma = leading_comma
 539        self.max_text_width = max_text_width
 540        self.comments = comments
 541        self.dialect = Dialect.get_or_raise(dialect)
 542
 543        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 544        self.normalize_functions = (
 545            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 546        )
 547
 548        self.unsupported_messages: t.List[str] = []
 549        self._escaped_quote_end: str = (
 550            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 551        )
 552        self._escaped_identifier_end: str = (
 553            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
 554        )
 555
 556    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 557        """
 558        Generates the SQL string corresponding to the given syntax tree.
 559
 560        Args:
 561            expression: The syntax tree.
 562            copy: Whether to copy the expression. The generator performs mutations so
 563                it is safer to copy.
 564
 565        Returns:
 566            The SQL string corresponding to `expression`.
 567        """
 568        if copy:
 569            expression = expression.copy()
 570
 571        expression = self.preprocess(expression)
 572
 573        self.unsupported_messages = []
 574        sql = self.sql(expression).strip()
 575
 576        if self.pretty:
 577            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 578
 579        if self.unsupported_level == ErrorLevel.IGNORE:
 580            return sql
 581
 582        if self.unsupported_level == ErrorLevel.WARN:
 583            for msg in self.unsupported_messages:
 584                logger.warning(msg)
 585        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 586            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 587
 588        return sql
 589
 590    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 591        """Apply generic preprocessing transformations to a given expression."""
 592        if (
 593            not expression.parent
 594            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 595            and any(node.parent is not expression for node in expression.find_all(exp.With))
 596        ):
 597            from sqlglot.transforms import move_ctes_to_top_level
 598
 599            expression = move_ctes_to_top_level(expression)
 600
 601        if self.ENSURE_BOOLS:
 602            from sqlglot.transforms import ensure_bools
 603
 604            expression = ensure_bools(expression)
 605
 606        return expression
 607
 608    def unsupported(self, message: str) -> None:
 609        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 610            raise UnsupportedError(message)
 611        self.unsupported_messages.append(message)
 612
 613    def sep(self, sep: str = " ") -> str:
 614        return f"{sep.strip()}\n" if self.pretty else sep
 615
 616    def seg(self, sql: str, sep: str = " ") -> str:
 617        return f"{self.sep(sep)}{sql}"
 618
 619    def pad_comment(self, comment: str) -> str:
 620        comment = " " + comment if comment[0].strip() else comment
 621        comment = comment + " " if comment[-1].strip() else comment
 622        return comment
 623
 624    def maybe_comment(
 625        self,
 626        sql: str,
 627        expression: t.Optional[exp.Expression] = None,
 628        comments: t.Optional[t.List[str]] = None,
 629    ) -> str:
 630        comments = (
 631            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 632            if self.comments
 633            else None
 634        )
 635
 636        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 637            return sql
 638
 639        comments_sql = " ".join(
 640            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 641        )
 642
 643        if not comments_sql:
 644            return sql
 645
 646        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 647            return (
 648                f"{self.sep()}{comments_sql}{sql}"
 649                if sql[0].isspace()
 650                else f"{comments_sql}{self.sep()}{sql}"
 651            )
 652
 653        return f"{sql} {comments_sql}"
 654
 655    def wrap(self, expression: exp.Expression | str) -> str:
 656        this_sql = self.indent(
 657            (
 658                self.sql(expression)
 659                if isinstance(expression, exp.UNWRAPPED_QUERIES)
 660                else self.sql(expression, "this")
 661            ),
 662            level=1,
 663            pad=0,
 664        )
 665        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 666
 667    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 668        original = self.identify
 669        self.identify = False
 670        result = func(*args, **kwargs)
 671        self.identify = original
 672        return result
 673
 674    def normalize_func(self, name: str) -> str:
 675        if self.normalize_functions == "upper" or self.normalize_functions is True:
 676            return name.upper()
 677        if self.normalize_functions == "lower":
 678            return name.lower()
 679        return name
 680
 681    def indent(
 682        self,
 683        sql: str,
 684        level: int = 0,
 685        pad: t.Optional[int] = None,
 686        skip_first: bool = False,
 687        skip_last: bool = False,
 688    ) -> str:
 689        if not self.pretty:
 690            return sql
 691
 692        pad = self.pad if pad is None else pad
 693        lines = sql.split("\n")
 694
 695        return "\n".join(
 696            (
 697                line
 698                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 699                else f"{' ' * (level * self._indent + pad)}{line}"
 700            )
 701            for i, line in enumerate(lines)
 702        )
 703
 704    def sql(
 705        self,
 706        expression: t.Optional[str | exp.Expression],
 707        key: t.Optional[str] = None,
 708        comment: bool = True,
 709    ) -> str:
 710        if not expression:
 711            return ""
 712
 713        if isinstance(expression, str):
 714            return expression
 715
 716        if key:
 717            value = expression.args.get(key)
 718            if value:
 719                return self.sql(value)
 720            return ""
 721
 722        transform = self.TRANSFORMS.get(expression.__class__)
 723
 724        if callable(transform):
 725            sql = transform(self, expression)
 726        elif isinstance(expression, exp.Expression):
 727            exp_handler_name = f"{expression.key}_sql"
 728
 729            if hasattr(self, exp_handler_name):
 730                sql = getattr(self, exp_handler_name)(expression)
 731            elif isinstance(expression, exp.Func):
 732                sql = self.function_fallback_sql(expression)
 733            elif isinstance(expression, exp.Property):
 734                sql = self.property_sql(expression)
 735            else:
 736                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 737        else:
 738            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 739
 740        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 741
 742    def uncache_sql(self, expression: exp.Uncache) -> str:
 743        table = self.sql(expression, "this")
 744        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 745        return f"UNCACHE TABLE{exists_sql} {table}"
 746
 747    def cache_sql(self, expression: exp.Cache) -> str:
 748        lazy = " LAZY" if expression.args.get("lazy") else ""
 749        table = self.sql(expression, "this")
 750        options = expression.args.get("options")
 751        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 752        sql = self.sql(expression, "expression")
 753        sql = f" AS{self.sep()}{sql}" if sql else ""
 754        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 755        return self.prepend_ctes(expression, sql)
 756
 757    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 758        if isinstance(expression.parent, exp.Cast):
 759            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 760        default = "DEFAULT " if expression.args.get("default") else ""
 761        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 762
 763    def column_sql(self, expression: exp.Column) -> str:
 764        join_mark = " (+)" if expression.args.get("join_mark") else ""
 765
 766        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
 767            join_mark = ""
 768            self.unsupported("Outer join syntax using the (+) operator is not supported.")
 769
 770        column = ".".join(
 771            self.sql(part)
 772            for part in (
 773                expression.args.get("catalog"),
 774                expression.args.get("db"),
 775                expression.args.get("table"),
 776                expression.args.get("this"),
 777            )
 778            if part
 779        )
 780
 781        return f"{column}{join_mark}"
 782
 783    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
 784        this = self.sql(expression, "this")
 785        this = f" {this}" if this else ""
 786        position = self.sql(expression, "position")
 787        return f"{position}{this}"
 788
 789    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
 790        column = self.sql(expression, "this")
 791        kind = self.sql(expression, "kind")
 792        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 793        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 794        kind = f"{sep}{kind}" if kind else ""
 795        constraints = f" {constraints}" if constraints else ""
 796        position = self.sql(expression, "position")
 797        position = f" {position}" if position else ""
 798
 799        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
 800            kind = ""
 801
 802        return f"{exists}{column}{kind}{constraints}{position}"
 803
 804    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 805        this = self.sql(expression, "this")
 806        kind_sql = self.sql(expression, "kind").strip()
 807        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 808
 809    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
 810        this = self.sql(expression, "this")
 811        if expression.args.get("not_null"):
 812            persisted = " PERSISTED NOT NULL"
 813        elif expression.args.get("persisted"):
 814            persisted = " PERSISTED"
 815        else:
 816            persisted = ""
 817        return f"AS {this}{persisted}"
 818
 819    def autoincrementcolumnconstraint_sql(self, _) -> str:
 820        return self.token_sql(TokenType.AUTO_INCREMENT)
 821
 822    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
 823        if isinstance(expression.this, list):
 824            this = self.wrap(self.expressions(expression, key="this", flat=True))
 825        else:
 826            this = self.sql(expression, "this")
 827
 828        return f"COMPRESS {this}"
 829
 830    def generatedasidentitycolumnconstraint_sql(
 831        self, expression: exp.GeneratedAsIdentityColumnConstraint
 832    ) -> str:
 833        this = ""
 834        if expression.this is not None:
 835            on_null = " ON NULL" if expression.args.get("on_null") else ""
 836            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
 837
 838        start = expression.args.get("start")
 839        start = f"START WITH {start}" if start else ""
 840        increment = expression.args.get("increment")
 841        increment = f" INCREMENT BY {increment}" if increment else ""
 842        minvalue = expression.args.get("minvalue")
 843        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 844        maxvalue = expression.args.get("maxvalue")
 845        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 846        cycle = expression.args.get("cycle")
 847        cycle_sql = ""
 848
 849        if cycle is not None:
 850            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
 851            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
 852
 853        sequence_opts = ""
 854        if start or increment or cycle_sql:
 855            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
 856            sequence_opts = f" ({sequence_opts.strip()})"
 857
 858        expr = self.sql(expression, "expression")
 859        expr = f"({expr})" if expr else "IDENTITY"
 860
 861        return f"GENERATED{this} AS {expr}{sequence_opts}"
 862
 863    def generatedasrowcolumnconstraint_sql(
 864        self, expression: exp.GeneratedAsRowColumnConstraint
 865    ) -> str:
 866        start = "START" if expression.args.get("start") else "END"
 867        hidden = " HIDDEN" if expression.args.get("hidden") else ""
 868        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
 869
 870    def periodforsystemtimeconstraint_sql(
 871        self, expression: exp.PeriodForSystemTimeConstraint
 872    ) -> str:
 873        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
 874
 875    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
 876        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
 877
 878    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
 879        return f"AS {self.sql(expression, 'this')}"
 880
 881    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
 882        desc = expression.args.get("desc")
 883        if desc is not None:
 884            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
 885        return "PRIMARY KEY"
 886
 887    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
 888        this = self.sql(expression, "this")
 889        this = f" {this}" if this else ""
 890        index_type = expression.args.get("index_type")
 891        index_type = f" USING {index_type}" if index_type else ""
 892        on_conflict = self.sql(expression, "on_conflict")
 893        on_conflict = f" {on_conflict}" if on_conflict else ""
 894        return f"UNIQUE{this}{index_type}{on_conflict}"
 895
 896    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
 897        return self.sql(expression, "this")
 898
 899    def create_sql(self, expression: exp.Create) -> str:
 900        kind = self.sql(expression, "kind")
 901        properties = expression.args.get("properties")
 902        properties_locs = self.locate_properties(properties) if properties else defaultdict()
 903
 904        this = self.createable_sql(expression, properties_locs)
 905
 906        properties_sql = ""
 907        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
 908            exp.Properties.Location.POST_WITH
 909        ):
 910            properties_sql = self.sql(
 911                exp.Properties(
 912                    expressions=[
 913                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
 914                        *properties_locs[exp.Properties.Location.POST_WITH],
 915                    ]
 916                )
 917            )
 918
 919        begin = " BEGIN" if expression.args.get("begin") else ""
 920        end = " END" if expression.args.get("end") else ""
 921
 922        expression_sql = self.sql(expression, "expression")
 923        if expression_sql:
 924            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
 925
 926            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
 927                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
 928                    postalias_props_sql = self.properties(
 929                        exp.Properties(
 930                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
 931                        ),
 932                        wrapped=False,
 933                    )
 934                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
 935                else:
 936                    expression_sql = f" AS{expression_sql}"
 937
 938        postindex_props_sql = ""
 939        if properties_locs.get(exp.Properties.Location.POST_INDEX):
 940            postindex_props_sql = self.properties(
 941                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
 942                wrapped=False,
 943                prefix=" ",
 944            )
 945
 946        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
 947        indexes = f" {indexes}" if indexes else ""
 948        index_sql = indexes + postindex_props_sql
 949
 950        replace = " OR REPLACE" if expression.args.get("replace") else ""
 951        unique = " UNIQUE" if expression.args.get("unique") else ""
 952
 953        postcreate_props_sql = ""
 954        if properties_locs.get(exp.Properties.Location.POST_CREATE):
 955            postcreate_props_sql = self.properties(
 956                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
 957                sep=" ",
 958                prefix=" ",
 959                wrapped=False,
 960            )
 961
 962        modifiers = "".join((replace, unique, postcreate_props_sql))
 963
 964        postexpression_props_sql = ""
 965        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
 966            postexpression_props_sql = self.properties(
 967                exp.Properties(
 968                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
 969                ),
 970                sep=" ",
 971                prefix=" ",
 972                wrapped=False,
 973            )
 974
 975        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
 976        no_schema_binding = (
 977            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
 978        )
 979
 980        clone = self.sql(expression, "clone")
 981        clone = f" {clone}" if clone else ""
 982
 983        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
 984        return self.prepend_ctes(expression, expression_sql)
 985
 986    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
 987        start = self.sql(expression, "start")
 988        start = f"START WITH {start}" if start else ""
 989        increment = self.sql(expression, "increment")
 990        increment = f" INCREMENT BY {increment}" if increment else ""
 991        minvalue = self.sql(expression, "minvalue")
 992        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 993        maxvalue = self.sql(expression, "maxvalue")
 994        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 995        owned = self.sql(expression, "owned")
 996        owned = f" OWNED BY {owned}" if owned else ""
 997
 998        cache = expression.args.get("cache")
 999        if cache is None:
1000            cache_str = ""
1001        elif cache is True:
1002            cache_str = " CACHE"
1003        else:
1004            cache_str = f" CACHE {cache}"
1005
1006        options = self.expressions(expression, key="options", flat=True, sep=" ")
1007        options = f" {options}" if options else ""
1008
1009        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1010
1011    def clone_sql(self, expression: exp.Clone) -> str:
1012        this = self.sql(expression, "this")
1013        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1014        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1015        return f"{shallow}{keyword} {this}"
1016
1017    def describe_sql(self, expression: exp.Describe) -> str:
1018        extended = " EXTENDED" if expression.args.get("extended") else ""
1019        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
1020
1021    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1022        tag = self.sql(expression, "tag")
1023        return f"${tag}${self.sql(expression, 'this')}${tag}$"
1024
1025    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1026        with_ = self.sql(expression, "with")
1027        if with_:
1028            sql = f"{with_}{self.sep()}{sql}"
1029        return sql
1030
1031    def with_sql(self, expression: exp.With) -> str:
1032        sql = self.expressions(expression, flat=True)
1033        recursive = (
1034            "RECURSIVE "
1035            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1036            else ""
1037        )
1038
1039        return f"WITH {recursive}{sql}"
1040
1041    def cte_sql(self, expression: exp.CTE) -> str:
1042        alias = self.sql(expression, "alias")
1043        return f"{alias} AS {self.wrap(expression)}"
1044
1045    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1046        alias = self.sql(expression, "this")
1047        columns = self.expressions(expression, key="columns", flat=True)
1048        columns = f"({columns})" if columns else ""
1049
1050        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1051            columns = ""
1052            self.unsupported("Named columns are not supported in table alias.")
1053
1054        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1055            alias = "_t"
1056
1057        return f"{alias}{columns}"
1058
1059    def bitstring_sql(self, expression: exp.BitString) -> str:
1060        this = self.sql(expression, "this")
1061        if self.dialect.BIT_START:
1062            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1063        return f"{int(this, 2)}"
1064
1065    def hexstring_sql(self, expression: exp.HexString) -> str:
1066        this = self.sql(expression, "this")
1067        if self.dialect.HEX_START:
1068            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1069        return f"{int(this, 16)}"
1070
1071    def bytestring_sql(self, expression: exp.ByteString) -> str:
1072        this = self.sql(expression, "this")
1073        if self.dialect.BYTE_START:
1074            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1075        return this
1076
1077    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1078        this = self.sql(expression, "this")
1079        escape = expression.args.get("escape")
1080
1081        if self.dialect.UNICODE_START:
1082            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
1083            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
1084
1085        if escape:
1086            pattern = re.compile(rf"{escape.name}(\d+)")
1087        else:
1088            pattern = ESCAPED_UNICODE_RE
1089
1090        this = pattern.sub(r"\\u\1", this)
1091        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
1092
1093    def rawstring_sql(self, expression: exp.RawString) -> str:
1094        string = self.escape_str(expression.this.replace("\\", "\\\\"))
1095        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1096
1097    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1098        this = self.sql(expression, "this")
1099        specifier = self.sql(expression, "expression")
1100        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1101        return f"{this}{specifier}"
1102
1103    def datatype_sql(self, expression: exp.DataType) -> str:
1104        type_value = expression.this
1105
1106        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1107            type_sql = self.sql(expression, "kind")
1108        else:
1109            type_sql = (
1110                self.TYPE_MAPPING.get(type_value, type_value.value)
1111                if isinstance(type_value, exp.DataType.Type)
1112                else type_value
1113            )
1114
1115        nested = ""
1116        interior = self.expressions(expression, flat=True)
1117        values = ""
1118
1119        if interior:
1120            if expression.args.get("nested"):
1121                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1122                if expression.args.get("values") is not None:
1123                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1124                    values = self.expressions(expression, key="values", flat=True)
1125                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1126            elif type_value == exp.DataType.Type.INTERVAL:
1127                nested = f" {interior}"
1128            else:
1129                nested = f"({interior})"
1130
1131        type_sql = f"{type_sql}{nested}{values}"
1132        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1133            exp.DataType.Type.TIMETZ,
1134            exp.DataType.Type.TIMESTAMPTZ,
1135        ):
1136            type_sql = f"{type_sql} WITH TIME ZONE"
1137
1138        return type_sql
1139
1140    def directory_sql(self, expression: exp.Directory) -> str:
1141        local = "LOCAL " if expression.args.get("local") else ""
1142        row_format = self.sql(expression, "row_format")
1143        row_format = f" {row_format}" if row_format else ""
1144        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1145
1146    def delete_sql(self, expression: exp.Delete) -> str:
1147        this = self.sql(expression, "this")
1148        this = f" FROM {this}" if this else ""
1149        using = self.sql(expression, "using")
1150        using = f" USING {using}" if using else ""
1151        where = self.sql(expression, "where")
1152        returning = self.sql(expression, "returning")
1153        limit = self.sql(expression, "limit")
1154        tables = self.expressions(expression, key="tables")
1155        tables = f" {tables}" if tables else ""
1156        if self.RETURNING_END:
1157            expression_sql = f"{this}{using}{where}{returning}{limit}"
1158        else:
1159            expression_sql = f"{returning}{this}{using}{where}{limit}"
1160        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1161
1162    def drop_sql(self, expression: exp.Drop) -> str:
1163        this = self.sql(expression, "this")
1164        kind = expression.args["kind"]
1165        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1166        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1167        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1168        cascade = " CASCADE" if expression.args.get("cascade") else ""
1169        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1170        purge = " PURGE" if expression.args.get("purge") else ""
1171        return (
1172            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
1173        )
1174
1175    def except_sql(self, expression: exp.Except) -> str:
1176        return self.set_operations(expression)
1177
1178    def except_op(self, expression: exp.Except) -> str:
1179        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
1180
1181    def fetch_sql(self, expression: exp.Fetch) -> str:
1182        direction = expression.args.get("direction")
1183        direction = f" {direction}" if direction else ""
1184        count = expression.args.get("count")
1185        count = f" {count}" if count else ""
1186        if expression.args.get("percent"):
1187            count = f"{count} PERCENT"
1188        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
1189        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1190
1191    def filter_sql(self, expression: exp.Filter) -> str:
1192        if self.AGGREGATE_FILTER_SUPPORTED:
1193            this = self.sql(expression, "this")
1194            where = self.sql(expression, "expression").strip()
1195            return f"{this} FILTER({where})"
1196
1197        agg = expression.this
1198        agg_arg = agg.this
1199        cond = expression.expression.this
1200        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1201        return self.sql(agg)
1202
1203    def hint_sql(self, expression: exp.Hint) -> str:
1204        if not self.QUERY_HINTS:
1205            self.unsupported("Hints are not supported")
1206            return ""
1207
1208        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1209
1210    def index_sql(self, expression: exp.Index) -> str:
1211        unique = "UNIQUE " if expression.args.get("unique") else ""
1212        primary = "PRIMARY " if expression.args.get("primary") else ""
1213        amp = "AMP " if expression.args.get("amp") else ""
1214        name = self.sql(expression, "this")
1215        name = f"{name} " if name else ""
1216        table = self.sql(expression, "table")
1217        table = f"{self.INDEX_ON} {table}" if table else ""
1218        using = self.sql(expression, "using")
1219        using = f" USING {using}" if using else ""
1220        index = "INDEX " if not table else ""
1221        columns = self.expressions(expression, key="columns", flat=True)
1222        columns = f"({columns})" if columns else ""
1223        partition_by = self.expressions(expression, key="partition_by", flat=True)
1224        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1225        where = self.sql(expression, "where")
1226        include = self.expressions(expression, key="include", flat=True)
1227        if include:
1228            include = f" INCLUDE ({include})"
1229        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"
1230
1231    def identifier_sql(self, expression: exp.Identifier) -> str:
1232        text = expression.name
1233        lower = text.lower()
1234        text = lower if self.normalize and not expression.quoted else text
1235        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
1236        if (
1237            expression.quoted
1238            or self.dialect.can_identify(text, self.identify)
1239            or lower in self.RESERVED_KEYWORDS
1240            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1241        ):
1242            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
1243        return text
1244
1245    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1246        input_format = self.sql(expression, "input_format")
1247        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1248        output_format = self.sql(expression, "output_format")
1249        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1250        return self.sep().join((input_format, output_format))
1251
1252    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1253        string = self.sql(exp.Literal.string(expression.name))
1254        return f"{prefix}{string}"
1255
1256    def partition_sql(self, expression: exp.Partition) -> str:
1257        return f"PARTITION({self.expressions(expression, flat=True)})"
1258
1259    def properties_sql(self, expression: exp.Properties) -> str:
1260        root_properties = []
1261        with_properties = []
1262
1263        for p in expression.expressions:
1264            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1265            if p_loc == exp.Properties.Location.POST_WITH:
1266                with_properties.append(p)
1267            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1268                root_properties.append(p)
1269
1270        return self.root_properties(
1271            exp.Properties(expressions=root_properties)
1272        ) + self.with_properties(exp.Properties(expressions=with_properties))
1273
1274    def root_properties(self, properties: exp.Properties) -> str:
1275        if properties.expressions:
1276            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1277        return ""
1278
1279    def properties(
1280        self,
1281        properties: exp.Properties,
1282        prefix: str = "",
1283        sep: str = ", ",
1284        suffix: str = "",
1285        wrapped: bool = True,
1286    ) -> str:
1287        if properties.expressions:
1288            expressions = self.expressions(properties, sep=sep, indent=False)
1289            if expressions:
1290                expressions = self.wrap(expressions) if wrapped else expressions
1291                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1292        return ""
1293
1294    def with_properties(self, properties: exp.Properties) -> str:
1295        return self.properties(properties, prefix=self.seg("WITH"))
1296
1297    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1298        properties_locs = defaultdict(list)
1299        for p in properties.expressions:
1300            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1301            if p_loc != exp.Properties.Location.UNSUPPORTED:
1302                properties_locs[p_loc].append(p)
1303            else:
1304                self.unsupported(f"Unsupported property {p.key}")
1305
1306        return properties_locs
1307
1308    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1309        if isinstance(expression.this, exp.Dot):
1310            return self.sql(expression, "this")
1311        return f"'{expression.name}'" if string_key else expression.name
1312
1313    def property_sql(self, expression: exp.Property) -> str:
1314        property_cls = expression.__class__
1315        if property_cls == exp.Property:
1316            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1317
1318        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1319        if not property_name:
1320            self.unsupported(f"Unsupported property {expression.key}")
1321
1322        return f"{property_name}={self.sql(expression, 'this')}"
1323
1324    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1325        if self.SUPPORTS_CREATE_TABLE_LIKE:
1326            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1327            options = f" {options}" if options else ""
1328
1329            like = f"LIKE {self.sql(expression, 'this')}{options}"
1330            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1331                like = f"({like})"
1332
1333            return like
1334
1335        if expression.expressions:
1336            self.unsupported("Transpilation of LIKE property options is unsupported")
1337
1338        select = exp.select("*").from_(expression.this).limit(0)
1339        return f"AS {self.sql(select)}"
1340
1341    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1342        no = "NO " if expression.args.get("no") else ""
1343        protection = " PROTECTION" if expression.args.get("protection") else ""
1344        return f"{no}FALLBACK{protection}"
1345
1346    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1347        no = "NO " if expression.args.get("no") else ""
1348        local = expression.args.get("local")
1349        local = f"{local} " if local else ""
1350        dual = "DUAL " if expression.args.get("dual") else ""
1351        before = "BEFORE " if expression.args.get("before") else ""
1352        after = "AFTER " if expression.args.get("after") else ""
1353        return f"{no}{local}{dual}{before}{after}JOURNAL"
1354
1355    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1356        freespace = self.sql(expression, "this")
1357        percent = " PERCENT" if expression.args.get("percent") else ""
1358        return f"FREESPACE={freespace}{percent}"
1359
1360    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1361        if expression.args.get("default"):
1362            property = "DEFAULT"
1363        elif expression.args.get("on"):
1364            property = "ON"
1365        else:
1366            property = "OFF"
1367        return f"CHECKSUM={property}"
1368
1369    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1370        if expression.args.get("no"):
1371            return "NO MERGEBLOCKRATIO"
1372        if expression.args.get("default"):
1373            return "DEFAULT MERGEBLOCKRATIO"
1374
1375        percent = " PERCENT" if expression.args.get("percent") else ""
1376        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1377
1378    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1379        default = expression.args.get("default")
1380        minimum = expression.args.get("minimum")
1381        maximum = expression.args.get("maximum")
1382        if default or minimum or maximum:
1383            if default:
1384                prop = "DEFAULT"
1385            elif minimum:
1386                prop = "MINIMUM"
1387            else:
1388                prop = "MAXIMUM"
1389            return f"{prop} DATABLOCKSIZE"
1390        units = expression.args.get("units")
1391        units = f" {units}" if units else ""
1392        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1393
1394    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1395        autotemp = expression.args.get("autotemp")
1396        always = expression.args.get("always")
1397        default = expression.args.get("default")
1398        manual = expression.args.get("manual")
1399        never = expression.args.get("never")
1400
1401        if autotemp is not None:
1402            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1403        elif always:
1404            prop = "ALWAYS"
1405        elif default:
1406            prop = "DEFAULT"
1407        elif manual:
1408            prop = "MANUAL"
1409        elif never:
1410            prop = "NEVER"
1411        return f"BLOCKCOMPRESSION={prop}"
1412
1413    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1414        no = expression.args.get("no")
1415        no = " NO" if no else ""
1416        concurrent = expression.args.get("concurrent")
1417        concurrent = " CONCURRENT" if concurrent else ""
1418
1419        for_ = ""
1420        if expression.args.get("for_all"):
1421            for_ = " FOR ALL"
1422        elif expression.args.get("for_insert"):
1423            for_ = " FOR INSERT"
1424        elif expression.args.get("for_none"):
1425            for_ = " FOR NONE"
1426        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
1427
1428    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1429        if isinstance(expression.this, list):
1430            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1431        if expression.this:
1432            modulus = self.sql(expression, "this")
1433            remainder = self.sql(expression, "expression")
1434            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1435
1436        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1437        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1438        return f"FROM ({from_expressions}) TO ({to_expressions})"
1439
1440    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1441        this = self.sql(expression, "this")
1442
1443        for_values_or_default = expression.expression
1444        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1445            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1446        else:
1447            for_values_or_default = " DEFAULT"
1448
1449        return f"PARTITION OF {this}{for_values_or_default}"
1450
1451    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1452        kind = expression.args.get("kind")
1453        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1454        for_or_in = expression.args.get("for_or_in")
1455        for_or_in = f" {for_or_in}" if for_or_in else ""
1456        lock_type = expression.args.get("lock_type")
1457        override = " OVERRIDE" if expression.args.get("override") else ""
1458        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1459
1460    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1461        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1462        statistics = expression.args.get("statistics")
1463        statistics_sql = ""
1464        if statistics is not None:
1465            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1466        return f"{data_sql}{statistics_sql}"
1467
1468    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1469        sql = "WITH(SYSTEM_VERSIONING=ON"
1470
1471        if expression.this:
1472            history_table = self.sql(expression, "this")
1473            sql = f"{sql}(HISTORY_TABLE={history_table}"
1474
1475            if expression.expression:
1476                data_consistency_check = self.sql(expression, "expression")
1477                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
1478
1479            sql = f"{sql})"
1480
1481        return f"{sql})"
1482
1483    def insert_sql(self, expression: exp.Insert) -> str:
1484        hint = self.sql(expression, "hint")
1485        overwrite = expression.args.get("overwrite")
1486
1487        if isinstance(expression.this, exp.Directory):
1488            this = " OVERWRITE" if overwrite else " INTO"
1489        else:
1490            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1491
1492        alternative = expression.args.get("alternative")
1493        alternative = f" OR {alternative}" if alternative else ""
1494        ignore = " IGNORE" if expression.args.get("ignore") else ""
1495
1496        this = f"{this} {self.sql(expression, 'this')}"
1497
1498        exists = " IF EXISTS" if expression.args.get("exists") else ""
1499        partition_sql = (
1500            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1501        )
1502        where = self.sql(expression, "where")
1503        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1504        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1505        on_conflict = self.sql(expression, "conflict")
1506        on_conflict = f" {on_conflict}" if on_conflict else ""
1507        by_name = " BY NAME" if expression.args.get("by_name") else ""
1508        returning = self.sql(expression, "returning")
1509
1510        if self.RETURNING_END:
1511            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1512        else:
1513            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1514
1515        sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1516        return self.prepend_ctes(expression, sql)
1517
1518    def intersect_sql(self, expression: exp.Intersect) -> str:
1519        return self.set_operations(expression)
1520
1521    def intersect_op(self, expression: exp.Intersect) -> str:
1522        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
1523
1524    def introducer_sql(self, expression: exp.Introducer) -> str:
1525        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1526
1527    def kill_sql(self, expression: exp.Kill) -> str:
1528        kind = self.sql(expression, "kind")
1529        kind = f" {kind}" if kind else ""
1530        this = self.sql(expression, "this")
1531        this = f" {this}" if this else ""
1532        return f"KILL{kind}{this}"
1533
1534    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1535        return expression.name
1536
1537    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1538        return expression.name
1539
1540    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1541        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1542
1543        constraint = self.sql(expression, "constraint")
1544        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1545
1546        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1547        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1548        action = self.sql(expression, "action")
1549
1550        expressions = self.expressions(expression, flat=True)
1551        if expressions:
1552            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1553            expressions = f" {set_keyword}{expressions}"
1554
1555        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
1556
1557    def returning_sql(self, expression: exp.Returning) -> str:
1558        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1559
1560    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1561        fields = expression.args.get("fields")
1562        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1563        escaped = expression.args.get("escaped")
1564        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1565        items = expression.args.get("collection_items")
1566        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1567        keys = expression.args.get("map_keys")
1568        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1569        lines = expression.args.get("lines")
1570        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1571        null = expression.args.get("null")
1572        null = f" NULL DEFINED AS {null}" if null else ""
1573        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1574
1575    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1576        return f"WITH ({self.expressions(expression, flat=True)})"
1577
1578    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1579        this = f"{self.sql(expression, 'this')} INDEX"
1580        target = self.sql(expression, "target")
1581        target = f" FOR {target}" if target else ""
1582        return f"{this}{target} ({self.expressions(expression, flat=True)})"
1583
1584    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1585        this = self.sql(expression, "this")
1586        kind = self.sql(expression, "kind")
1587        expr = self.sql(expression, "expression")
1588        return f"{this} ({kind} => {expr})"
1589
1590    def table_parts(self, expression: exp.Table) -> str:
1591        return ".".join(
1592            self.sql(part)
1593            for part in (
1594                expression.args.get("catalog"),
1595                expression.args.get("db"),
1596                expression.args.get("this"),
1597            )
1598            if part is not None
1599        )
1600
1601    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1602        table = self.table_parts(expression)
1603        only = "ONLY " if expression.args.get("only") else ""
1604        version = self.sql(expression, "version")
1605        version = f" {version}" if version else ""
1606        alias = self.sql(expression, "alias")
1607        alias = f"{sep}{alias}" if alias else ""
1608        hints = self.expressions(expression, key="hints", sep=" ")
1609        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1610        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1611        pivots = f" {pivots}" if pivots else ""
1612        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1613        laterals = self.expressions(expression, key="laterals", sep="")
1614
1615        file_format = self.sql(expression, "format")
1616        if file_format:
1617            pattern = self.sql(expression, "pattern")
1618            pattern = f", PATTERN => {pattern}" if pattern else ""
1619            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1620
1621        ordinality = expression.args.get("ordinality") or ""
1622        if ordinality:
1623            ordinality = f" WITH ORDINALITY{alias}"
1624            alias = ""
1625
1626        when = self.sql(expression, "when")
1627        if when:
1628            table = f"{table} {when}"
1629
1630        return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
1631
1632    def tablesample_sql(
1633        self,
1634        expression: exp.TableSample,
1635        sep: str = " AS ",
1636        tablesample_keyword: t.Optional[str] = None,
1637    ) -> str:
1638        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1639            table = expression.this.copy()
1640            table.set("alias", None)
1641            this = self.sql(table)
1642            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1643        else:
1644            this = self.sql(expression, "this")
1645            alias = ""
1646
1647        method = self.sql(expression, "method")
1648        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1649        numerator = self.sql(expression, "bucket_numerator")
1650        denominator = self.sql(expression, "bucket_denominator")
1651        field = self.sql(expression, "bucket_field")
1652        field = f" ON {field}" if field else ""
1653        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1654        seed = self.sql(expression, "seed")
1655        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1656
1657        size = self.sql(expression, "size")
1658        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1659            size = f"{size} ROWS"
1660
1661        percent = self.sql(expression, "percent")
1662        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1663            percent = f"{percent} PERCENT"
1664
1665        expr = f"{bucket}{percent}{size}"
1666        if self.TABLESAMPLE_REQUIRES_PARENS:
1667            expr = f"({expr})"
1668
1669        return (
1670            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1671        )
1672
1673    def pivot_sql(self, expression: exp.Pivot) -> str:
1674        expressions = self.expressions(expression, flat=True)
1675
1676        if expression.this:
1677            this = self.sql(expression, "this")
1678            if not expressions:
1679                return f"UNPIVOT {this}"
1680
1681            on = f"{self.seg('ON')} {expressions}"
1682            using = self.expressions(expression, key="using", flat=True)
1683            using = f"{self.seg('USING')} {using}" if using else ""
1684            group = self.sql(expression, "group")
1685            return f"PIVOT {this}{on}{using}{group}"
1686
1687        alias = self.sql(expression, "alias")
1688        alias = f" AS {alias}" if alias else ""
1689        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1690        field = self.sql(expression, "field")
1691        include_nulls = expression.args.get("include_nulls")
1692        if include_nulls is not None:
1693            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1694        else:
1695            nulls = ""
1696        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1697
1698    def version_sql(self, expression: exp.Version) -> str:
1699        this = f"FOR {expression.name}"
1700        kind = expression.text("kind")
1701        expr = self.sql(expression, "expression")
1702        return f"{this} {kind} {expr}"
1703
1704    def tuple_sql(self, expression: exp.Tuple) -> str:
1705        return f"({self.expressions(expression, flat=True)})"
1706
1707    def update_sql(self, expression: exp.Update) -> str:
1708        this = self.sql(expression, "this")
1709        set_sql = self.expressions(expression, flat=True)
1710        from_sql = self.sql(expression, "from")
1711        where_sql = self.sql(expression, "where")
1712        returning = self.sql(expression, "returning")
1713        order = self.sql(expression, "order")
1714        limit = self.sql(expression, "limit")
1715        if self.RETURNING_END:
1716            expression_sql = f"{from_sql}{where_sql}{returning}"
1717        else:
1718            expression_sql = f"{returning}{from_sql}{where_sql}"
1719        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1720        return self.prepend_ctes(expression, sql)
1721
1722    def values_sql(self, expression: exp.Values) -> str:
1723        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1724        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1725            args = self.expressions(expression)
1726            alias = self.sql(expression, "alias")
1727            values = f"VALUES{self.seg('')}{args}"
1728            values = (
1729                f"({values})"
1730                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1731                else values
1732            )
1733            return f"{values} AS {alias}" if alias else values
1734
1735        # Converts `VALUES...` expression into a series of select unions.
1736        alias_node = expression.args.get("alias")
1737        column_names = alias_node and alias_node.columns
1738
1739        selects: t.List[exp.Query] = []
1740
1741        for i, tup in enumerate(expression.expressions):
1742            row = tup.expressions
1743
1744            if i == 0 and column_names:
1745                row = [
1746                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1747                ]
1748
1749            selects.append(exp.Select(expressions=row))
1750
1751        if self.pretty:
1752            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1753            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1754            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1755            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1756            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
1757
1758        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
1759        unions = " UNION ALL ".join(self.sql(select) for select in selects)
1760        return f"({unions}){alias}"
1761
1762    def var_sql(self, expression: exp.Var) -> str:
1763        return self.sql(expression, "this")
1764
1765    def into_sql(self, expression: exp.Into) -> str:
1766        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1767        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1768        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
1769
1770    def from_sql(self, expression: exp.From) -> str:
1771        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
1772
1773    def group_sql(self, expression: exp.Group) -> str:
1774        group_by = self.op_expressions("GROUP BY", expression)
1775
1776        if expression.args.get("all"):
1777            return f"{group_by} ALL"
1778
1779        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1780        grouping_sets = (
1781            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1782        )
1783
1784        cube = expression.args.get("cube", [])
1785        if seq_get(cube, 0) is True:
1786            return f"{group_by}{self.seg('WITH CUBE')}"
1787        else:
1788            cube_sql = self.expressions(expression, key="cube", indent=False)
1789            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1790
1791        rollup = expression.args.get("rollup", [])
1792        if seq_get(rollup, 0) is True:
1793            return f"{group_by}{self.seg('WITH ROLLUP')}"
1794        else:
1795            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1796            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1797
1798        groupings = csv(
1799            grouping_sets,
1800            cube_sql,
1801            rollup_sql,
1802            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
1803            sep=self.GROUPINGS_SEP,
1804        )
1805
1806        if expression.args.get("expressions") and groupings:
1807            group_by = f"{group_by}{self.GROUPINGS_SEP}"
1808
1809        return f"{group_by}{groupings}"
1810
1811    def having_sql(self, expression: exp.Having) -> str:
1812        this = self.indent(self.sql(expression, "this"))
1813        return f"{self.seg('HAVING')}{self.sep()}{this}"
1814
1815    def connect_sql(self, expression: exp.Connect) -> str:
1816        start = self.sql(expression, "start")
1817        start = self.seg(f"START WITH {start}") if start else ""
1818        connect = self.sql(expression, "connect")
1819        connect = self.seg(f"CONNECT BY {connect}")
1820        return start + connect
1821
1822    def prior_sql(self, expression: exp.Prior) -> str:
1823        return f"PRIOR {self.sql(expression, 'this')}"
1824
1825    def join_sql(self, expression: exp.Join) -> str:
1826        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
1827            side = None
1828        else:
1829            side = expression.side
1830
1831        op_sql = " ".join(
1832            op
1833            for op in (
1834                expression.method,
1835                "GLOBAL" if expression.args.get("global") else None,
1836                side,
1837                expression.kind,
1838                expression.hint if self.JOIN_HINTS else None,
1839            )
1840            if op
1841        )
1842        on_sql = self.sql(expression, "on")
1843        using = expression.args.get("using")
1844
1845        if not on_sql and using:
1846            on_sql = csv(*(self.sql(column) for column in using))
1847
1848        this = expression.this
1849        this_sql = self.sql(this)
1850
1851        if on_sql:
1852            on_sql = self.indent(on_sql, skip_first=True)
1853            space = self.seg(" " * self.pad) if self.pretty else " "
1854            if using:
1855                on_sql = f"{space}USING ({on_sql})"
1856            else:
1857                on_sql = f"{space}ON {on_sql}"
1858        elif not op_sql:
1859            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
1860                return f" {this_sql}"
1861
1862            return f", {this_sql}"
1863
1864        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
1865        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
1866
1867    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1868        args = self.expressions(expression, flat=True)
1869        args = f"({args})" if len(args.split(",")) > 1 else args
1870        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
1871
1872    def lateral_op(self, expression: exp.Lateral) -> str:
1873        cross_apply = expression.args.get("cross_apply")
1874
1875        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
1876        if cross_apply is True:
1877            op = "INNER JOIN "
1878        elif cross_apply is False:
1879            op = "LEFT JOIN "
1880        else:
1881            op = ""
1882
1883        return f"{op}LATERAL"
1884
1885    def lateral_sql(self, expression: exp.Lateral) -> str:
1886        this = self.sql(expression, "this")
1887
1888        if expression.args.get("view"):
1889            alias = expression.args["alias"]
1890            columns = self.expressions(alias, key="columns", flat=True)
1891            table = f" {alias.name}" if alias.name else ""
1892            columns = f" AS {columns}" if columns else ""
1893            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1894            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1895
1896        alias = self.sql(expression, "alias")
1897        alias = f" AS {alias}" if alias else ""
1898        return f"{self.lateral_op(expression)} {this}{alias}"
1899
1900    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
1901        this = self.sql(expression, "this")
1902
1903        args = [
1904            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
1905            for e in (expression.args.get(k) for k in ("offset", "expression"))
1906            if e
1907        ]
1908
1909        args_sql = ", ".join(self.sql(e) for e in args)
1910        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
1911        expressions = self.expressions(expression, flat=True)
1912        expressions = f" BY {expressions}" if expressions else ""
1913
1914        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
1915
1916    def offset_sql(self, expression: exp.Offset) -> str:
1917        this = self.sql(expression, "this")
1918        value = expression.expression
1919        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
1920        expressions = self.expressions(expression, flat=True)
1921        expressions = f" BY {expressions}" if expressions else ""
1922        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
1923
1924    def setitem_sql(self, expression: exp.SetItem) -> str:
1925        kind = self.sql(expression, "kind")
1926        kind = f"{kind} " if kind else ""
1927        this = self.sql(expression, "this")
1928        expressions = self.expressions(expression)
1929        collate = self.sql(expression, "collate")
1930        collate = f" COLLATE {collate}" if collate else ""
1931        global_ = "GLOBAL " if expression.args.get("global") else ""
1932        return f"{global_}{kind}{this}{expressions}{collate}"
1933
1934    def set_sql(self, expression: exp.Set) -> str:
1935        expressions = (
1936            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
1937        )
1938        tag = " TAG" if expression.args.get("tag") else ""
1939        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
1940
1941    def pragma_sql(self, expression: exp.Pragma) -> str:
1942        return f"PRAGMA {self.sql(expression, 'this')}"
1943
1944    def lock_sql(self, expression: exp.Lock) -> str:
1945        if not self.LOCKING_READS_SUPPORTED:
1946            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1947            return ""
1948
1949        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
1950        expressions = self.expressions(expression, flat=True)
1951        expressions = f" OF {expressions}" if expressions else ""
1952        wait = expression.args.get("wait")
1953
1954        if wait is not None:
1955            if isinstance(wait, exp.Literal):
1956                wait = f" WAIT {self.sql(wait)}"
1957            else:
1958                wait = " NOWAIT" if wait else " SKIP LOCKED"
1959
1960        return f"{lock_type}{expressions}{wait or ''}"
1961
1962    def literal_sql(self, expression: exp.Literal) -> str:
1963        text = expression.this or ""
1964        if expression.is_string:
1965            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
1966        return text
1967
1968    def escape_str(self, text: str) -> str:
1969        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
1970        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
1971            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
1972        elif self.pretty:
1973            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1974        return text
1975
1976    def loaddata_sql(self, expression: exp.LoadData) -> str:
1977        local = " LOCAL" if expression.args.get("local") else ""
1978        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1979        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1980        this = f" INTO TABLE {self.sql(expression, 'this')}"
1981        partition = self.sql(expression, "partition")
1982        partition = f" {partition}" if partition else ""
1983        input_format = self.sql(expression, "input_format")
1984        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1985        serde = self.sql(expression, "serde")
1986        serde = f" SERDE {serde}" if serde else ""
1987        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
1988
1989    def null_sql(self, *_) -> str:
1990        return "NULL"
1991
1992    def boolean_sql(self, expression: exp.Boolean) -> str:
1993        return "TRUE" if expression.this else "FALSE"
1994
1995    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1996        this = self.sql(expression, "this")
1997        this = f"{this} " if this else this
1998        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
1999        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2000        interpolated_values = [
2001            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
2002            for named_expression in expression.args.get("interpolate") or []
2003        ]
2004        interpolate = (
2005            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2006        )
2007        return f"{order}{interpolate}"
2008
2009    def withfill_sql(self, expression: exp.WithFill) -> str:
2010        from_sql = self.sql(expression, "from")
2011        from_sql = f" FROM {from_sql}" if from_sql else ""
2012        to_sql = self.sql(expression, "to")
2013        to_sql = f" TO {to_sql}" if to_sql else ""
2014        step_sql = self.sql(expression, "step")
2015        step_sql = f" STEP {step_sql}" if step_sql else ""
2016        return f"WITH FILL{from_sql}{to_sql}{step_sql}"
2017
2018    def cluster_sql(self, expression: exp.Cluster) -> str:
2019        return self.op_expressions("CLUSTER BY", expression)
2020
2021    def distribute_sql(self, expression: exp.Distribute) -> str:
2022        return self.op_expressions("DISTRIBUTE BY", expression)
2023
2024    def sort_sql(self, expression: exp.Sort) -> str:
2025        return self.op_expressions("SORT BY", expression)
2026
2027    def ordered_sql(self, expression: exp.Ordered) -> str:
2028        desc = expression.args.get("desc")
2029        asc = not desc
2030
2031        nulls_first = expression.args.get("nulls_first")
2032        nulls_last = not nulls_first
2033        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2034        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2035        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2036
2037        this = self.sql(expression, "this")
2038
2039        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2040        nulls_sort_change = ""
2041        if nulls_first and (
2042            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2043        ):
2044            nulls_sort_change = " NULLS FIRST"
2045        elif (
2046            nulls_last
2047            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2048            and not nulls_are_last
2049        ):
2050            nulls_sort_change = " NULLS LAST"
2051
2052        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2053        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2054            window = expression.find_ancestor(exp.Window, exp.Select)
2055            if isinstance(window, exp.Window) and window.args.get("spec"):
2056                self.unsupported(
2057                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2058                )
2059                nulls_sort_change = ""
2060            elif self.NULL_ORDERING_SUPPORTED is None:
2061                if expression.this.is_int:
2062                    self.unsupported(
2063                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2064                    )
2065                else:
2066                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2067                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2068                nulls_sort_change = ""
2069
2070        with_fill = self.sql(expression, "with_fill")
2071        with_fill = f" {with_fill}" if with_fill else ""
2072
2073        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2074
2075    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2076        partition = self.partition_by_sql(expression)
2077        order = self.sql(expression, "order")
2078        measures = self.expressions(expression, key="measures")
2079        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2080        rows = self.sql(expression, "rows")
2081        rows = self.seg(rows) if rows else ""
2082        after = self.sql(expression, "after")
2083        after = self.seg(after) if after else ""
2084        pattern = self.sql(expression, "pattern")
2085        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2086        definition_sqls = [
2087            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2088            for definition in expression.args.get("define", [])
2089        ]
2090        definitions = self.expressions(sqls=definition_sqls)
2091        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2092        body = "".join(
2093            (
2094                partition,
2095                order,
2096                measures,
2097                rows,
2098                after,
2099                pattern,
2100                define,
2101            )
2102        )
2103        alias = self.sql(expression, "alias")
2104        alias = f" {alias}" if alias else ""
2105        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2106
2107    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2108        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
2109
2110        # If the limit is generated as TOP, we need to ensure it's not generated twice
2111        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
2112
2113        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2114            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2115        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2116            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2117
2118        fetch = isinstance(limit, exp.Fetch)
2119
2120        offset_limit_modifiers = (
2121            self.offset_limit_modifiers(expression, fetch, limit)
2122            if with_offset_limit_modifiers
2123            else []
2124        )
2125
2126        options = self.expressions(expression, key="options")
2127        if options:
2128            options = f" OPTION{self.wrap(options)}"
2129
2130        return csv(
2131            *sqls,
2132            *[self.sql(join) for join in expression.args.get("joins") or []],
2133            self.sql(expression, "connect"),
2134            self.sql(expression, "match"),
2135            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2136            self.sql(expression, "prewhere"),
2137            self.sql(expression, "where"),
2138            self.sql(expression, "group"),
2139            self.sql(expression, "having"),
2140            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2141            self.sql(expression, "order"),
2142            *offset_limit_modifiers,
2143            *self.after_limit_modifiers(expression),
2144            options,
2145            sep="",
2146        )
2147
2148    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2149        return ""
2150
2151    def offset_limit_modifiers(
2152        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2153    ) -> t.List[str]:
2154        return [
2155            self.sql(expression, "offset") if fetch else self.sql(limit),
2156            self.sql(limit) if fetch else self.sql(expression, "offset"),
2157        ]
2158
2159    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2160        locks = self.expressions(expression, key="locks", sep=" ")
2161        locks = f" {locks}" if locks else ""
2162        return [locks, self.sql(expression, "sample")]
2163
2164    def select_sql(self, expression: exp.Select) -> str:
2165        into = expression.args.get("into")
2166        if not self.SUPPORTS_SELECT_INTO and into:
2167            into.pop()
2168
2169        hint = self.sql(expression, "hint")
2170        distinct = self.sql(expression, "distinct")
2171        distinct = f" {distinct}" if distinct else ""
2172        kind = self.sql(expression, "kind")
2173        limit = expression.args.get("limit")
2174        top = (
2175            self.limit_sql(limit, top=True)
2176            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2177            else ""
2178        )
2179
2180        expressions = self.expressions(expression)
2181
2182        if kind:
2183            if kind in self.SELECT_KINDS:
2184                kind = f" AS {kind}"
2185            else:
2186                if kind == "STRUCT":
2187                    expressions = self.expressions(
2188                        sqls=[
2189                            self.sql(
2190                                exp.Struct(
2191                                    expressions=[
2192                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2193                                        if isinstance(e, exp.Alias)
2194                                        else e
2195                                        for e in expression.expressions
2196                                    ]
2197                                )
2198                            )
2199                        ]
2200                    )
2201                kind = ""
2202
2203        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2204        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2205        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2206        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2207        sql = self.query_modifiers(
2208            expression,
2209            f"SELECT{top_distinct}{kind}{expressions}",
2210            self.sql(expression, "into", comment=False),
2211            self.sql(expression, "from", comment=False),
2212        )
2213
2214        sql = self.prepend_ctes(expression, sql)
2215
2216        if not self.SUPPORTS_SELECT_INTO and into:
2217            if into.args.get("temporary"):
2218                table_kind = " TEMPORARY"
2219            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2220                table_kind = " UNLOGGED"
2221            else:
2222                table_kind = ""
2223            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2224
2225        return sql
2226
2227    def schema_sql(self, expression: exp.Schema) -> str:
2228        this = self.sql(expression, "this")
2229        sql = self.schema_columns_sql(expression)
2230        return f"{this} {sql}" if this and sql else this or sql
2231
2232    def schema_columns_sql(self, expression: exp.Schema) -> str:
2233        if expression.expressions:
2234            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2235        return ""
2236
2237    def star_sql(self, expression: exp.Star) -> str:
2238        except_ = self.expressions(expression, key="except", flat=True)
2239        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2240        replace = self.expressions(expression, key="replace", flat=True)
2241        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2242        return f"*{except_}{replace}"
2243
2244    def parameter_sql(self, expression: exp.Parameter) -> str:
2245        this = self.sql(expression, "this")
2246        return f"{self.PARAMETER_TOKEN}{this}"
2247
2248    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2249        this = self.sql(expression, "this")
2250        kind = expression.text("kind")
2251        if kind:
2252            kind = f"{kind}."
2253        return f"@@{kind}{this}"
2254
2255    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2256        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
2257
2258    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2259        alias = self.sql(expression, "alias")
2260        alias = f"{sep}{alias}" if alias else ""
2261
2262        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2263        pivots = f" {pivots}" if pivots else ""
2264
2265        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2266        return self.prepend_ctes(expression, sql)
2267
2268    def qualify_sql(self, expression: exp.Qualify) -> str:
2269        this = self.indent(self.sql(expression, "this"))
2270        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2271
2272    def set_operations(self, expression: exp.Union) -> str:
2273        sqls: t.List[str] = []
2274        stack: t.List[t.Union[str, exp.Expression]] = [expression]
2275
2276        while stack:
2277            node = stack.pop()
2278
2279            if isinstance(node, exp.Union):
2280                stack.append(node.expression)
2281                stack.append(
2282                    self.maybe_comment(
2283                        getattr(self, f"{node.key}_op")(node),
2284                        expression=node.this,
2285                        comments=node.comments,
2286                    )
2287                )
2288                stack.append(node.this)
2289            else:
2290                sqls.append(self.sql(node))
2291
2292        this = self.sep().join(sqls)
2293        this = self.query_modifiers(expression, this)
2294        return self.prepend_ctes(expression, this)
2295
2296    def union_sql(self, expression: exp.Union) -> str:
2297        return self.set_operations(expression)
2298
2299    def union_op(self, expression: exp.Union) -> str:
2300        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2301        kind = kind if expression.args.get("distinct") else " ALL"
2302        by_name = " BY NAME" if expression.args.get("by_name") else ""
2303        return f"UNION{kind}{by_name}"
2304
2305    def unnest_sql(self, expression: exp.Unnest) -> str:
2306        args = self.expressions(expression, flat=True)
2307
2308        alias = expression.args.get("alias")
2309        offset = expression.args.get("offset")
2310
2311        if self.UNNEST_WITH_ORDINALITY:
2312            if alias and isinstance(offset, exp.Expression):
2313                alias.append("columns", offset)
2314
2315        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2316            columns = alias.columns
2317            alias = self.sql(columns[0]) if columns else ""
2318        else:
2319            alias = self.sql(alias)
2320
2321        alias = f" AS {alias}" if alias else alias
2322        if self.UNNEST_WITH_ORDINALITY:
2323            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2324        else:
2325            if isinstance(offset, exp.Expression):
2326                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2327            elif offset:
2328                suffix = f"{alias} WITH OFFSET"
2329            else:
2330                suffix = alias
2331
2332        return f"UNNEST({args}){suffix}"
2333
2334    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2335        return ""
2336
2337    def where_sql(self, expression: exp.Where) -> str:
2338        this = self.indent(self.sql(expression, "this"))
2339        return f"{self.seg('WHERE')}{self.sep()}{this}"
2340
2341    def window_sql(self, expression: exp.Window) -> str:
2342        this = self.sql(expression, "this")
2343        partition = self.partition_by_sql(expression)
2344        order = expression.args.get("order")
2345        order = self.order_sql(order, flat=True) if order else ""
2346        spec = self.sql(expression, "spec")
2347        alias = self.sql(expression, "alias")
2348        over = self.sql(expression, "over") or "OVER"
2349
2350        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2351
2352        first = expression.args.get("first")
2353        if first is None:
2354            first = ""
2355        else:
2356            first = "FIRST" if first else "LAST"
2357
2358        if not partition and not order and not spec and alias:
2359            return f"{this} {alias}"
2360
2361        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2362        return f"{this} ({args})"
2363
2364    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2365        partition = self.expressions(expression, key="partition_by", flat=True)
2366        return f"PARTITION BY {partition}" if partition else ""
2367
2368    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2369        kind = self.sql(expression, "kind")
2370        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2371        end = (
2372            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2373            or "CURRENT ROW"
2374        )
2375        return f"{kind} BETWEEN {start} AND {end}"
2376
2377    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2378        this = self.sql(expression, "this")
2379        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2380        return f"{this} WITHIN GROUP ({expression_sql})"
2381
2382    def between_sql(self, expression: exp.Between) -> str:
2383        this = self.sql(expression, "this")
2384        low = self.sql(expression, "low")
2385        high = self.sql(expression, "high")
2386        return f"{this} BETWEEN {low} AND {high}"
2387
2388    def bracket_sql(self, expression: exp.Bracket) -> str:
2389        expressions = apply_index_offset(
2390            expression.this,
2391            expression.expressions,
2392            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2393        )
2394        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2395        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2396
2397    def all_sql(self, expression: exp.All) -> str:
2398        return f"ALL {self.wrap(expression)}"
2399
2400    def any_sql(self, expression: exp.Any) -> str:
2401        this = self.sql(expression, "this")
2402        if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2403            this = self.wrap(this)
2404        return f"ANY {this}"
2405
2406    def exists_sql(self, expression: exp.Exists) -> str:
2407        return f"EXISTS{self.wrap(expression)}"
2408
2409    def case_sql(self, expression: exp.Case) -> str:
2410        this = self.sql(expression, "this")
2411        statements = [f"CASE {this}" if this else "CASE"]
2412
2413        for e in expression.args["ifs"]:
2414            statements.append(f"WHEN {self.sql(e, 'this')}")
2415            statements.append(f"THEN {self.sql(e, 'true')}")
2416
2417        default = self.sql(expression, "default")
2418
2419        if default:
2420            statements.append(f"ELSE {default}")
2421
2422        statements.append("END")
2423
2424        if self.pretty and self.text_width(statements) > self.max_text_width:
2425            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2426
2427        return " ".join(statements)
2428
2429    def constraint_sql(self, expression: exp.Constraint) -> str:
2430        this = self.sql(expression, "this")
2431        expressions = self.expressions(expression, flat=True)
2432        return f"CONSTRAINT {this} {expressions}"
2433
2434    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2435        order = expression.args.get("order")
2436        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2437        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2438
2439    def extract_sql(self, expression: exp.Extract) -> str:
2440        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2441        expression_sql = self.sql(expression, "expression")
2442        return f"EXTRACT({this} FROM {expression_sql})"
2443
2444    def trim_sql(self, expression: exp.Trim) -> str:
2445        trim_type = self.sql(expression, "position")
2446
2447        if trim_type == "LEADING":
2448            return self.func("LTRIM", expression.this)
2449        elif trim_type == "TRAILING":
2450            return self.func("RTRIM", expression.this)
2451        else:
2452            return self.func("TRIM", expression.this, expression.expression)
2453
2454    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2455        args = expression.expressions
2456        if isinstance(expression, exp.ConcatWs):
2457            args = args[1:]  # Skip the delimiter
2458
2459        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2460            args = [exp.cast(e, "text") for e in args]
2461
2462        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2463            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2464
2465        return args
2466
2467    def concat_sql(self, expression: exp.Concat) -> str:
2468        expressions = self.convert_concat_args(expression)
2469
2470        # Some dialects don't allow a single-argument CONCAT call
2471        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2472            return self.sql(expressions[0])
2473
2474        return self.func("CONCAT", *expressions)
2475
2476    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2477        return self.func(
2478            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2479        )
2480
2481    def check_sql(self, expression: exp.Check) -> str:
2482        this = self.sql(expression, key="this")
2483        return f"CHECK ({this})"
2484
2485    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2486        expressions = self.expressions(expression, flat=True)
2487        reference = self.sql(expression, "reference")
2488        reference = f" {reference}" if reference else ""
2489        delete = self.sql(expression, "delete")
2490        delete = f" ON DELETE {delete}" if delete else ""
2491        update = self.sql(expression, "update")
2492        update = f" ON UPDATE {update}" if update else ""
2493        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2494
2495    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2496        expressions = self.expressions(expression, flat=True)
2497        options = self.expressions(expression, key="options", flat=True, sep=" ")
2498        options = f" {options}" if options else ""
2499        return f"PRIMARY KEY ({expressions}){options}"
2500
2501    def if_sql(self, expression: exp.If) -> str:
2502        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
2503
2504    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2505        modifier = expression.args.get("modifier")
2506        modifier = f" {modifier}" if modifier else ""
2507        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
2508
2509    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2510        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
2511
2512    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2513        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2514        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2515
2516    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2517        if isinstance(expression, exp.JSONPathPart):
2518            transform = self.TRANSFORMS.get(expression.__class__)
2519            if not callable(transform):
2520                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2521                return ""
2522
2523            return transform(self, expression)
2524
2525        if isinstance(expression, int):
2526            return str(expression)
2527
2528        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2529            escaped = expression.replace("'", "\\'")
2530            escaped = f"\\'{expression}\\'"
2531        else:
2532            escaped = expression.replace('"', '\\"')
2533            escaped = f'"{escaped}"'
2534
2535        return escaped
2536
2537    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2538        return f"{self.sql(expression, 'this')} FORMAT JSON"
2539
2540    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2541        null_handling = expression.args.get("null_handling")
2542        null_handling = f" {null_handling}" if null_handling else ""
2543
2544        unique_keys = expression.args.get("unique_keys")
2545        if unique_keys is not None:
2546            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2547        else:
2548            unique_keys = ""
2549
2550        return_type = self.sql(expression, "return_type")
2551        return_type = f" RETURNING {return_type}" if return_type else ""
2552        encoding = self.sql(expression, "encoding")
2553        encoding = f" ENCODING {encoding}" if encoding else ""
2554
2555        return self.func(
2556            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2557            *expression.expressions,
2558            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2559        )
2560
2561    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2562        return self.jsonobject_sql(expression)
2563
2564    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2565        null_handling = expression.args.get("null_handling")
2566        null_handling = f" {null_handling}" if null_handling else ""
2567        return_type = self.sql(expression, "return_type")
2568        return_type = f" RETURNING {return_type}" if return_type else ""
2569        strict = " STRICT" if expression.args.get("strict") else ""
2570        return self.func(
2571            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2572        )
2573
2574    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2575        this = self.sql(expression, "this")
2576        order = self.sql(expression, "order")
2577        null_handling = expression.args.get("null_handling")
2578        null_handling = f" {null_handling}" if null_handling else ""
2579        return_type = self.sql(expression, "return_type")
2580        return_type = f" RETURNING {return_type}" if return_type else ""
2581        strict = " STRICT" if expression.args.get("strict") else ""
2582        return self.func(
2583            "JSON_ARRAYAGG",
2584            this,
2585            suffix=f"{order}{null_handling}{return_type}{strict})",
2586        )
2587
2588    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2589        path = self.sql(expression, "path")
2590        path = f" PATH {path}" if path else ""
2591        nested_schema = self.sql(expression, "nested_schema")
2592
2593        if nested_schema:
2594            return f"NESTED{path} {nested_schema}"
2595
2596        this = self.sql(expression, "this")
2597        kind = self.sql(expression, "kind")
2598        kind = f" {kind}" if kind else ""
2599        return f"{this}{kind}{path}"
2600
2601    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2602        return self.func("COLUMNS", *expression.expressions)
2603
2604    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2605        this = self.sql(expression, "this")
2606        path = self.sql(expression, "path")
2607        path = f", {path}" if path else ""
2608        error_handling = expression.args.get("error_handling")
2609        error_handling = f" {error_handling}" if error_handling else ""
2610        empty_handling = expression.args.get("empty_handling")
2611        empty_handling = f" {empty_handling}" if empty_handling else ""
2612        schema = self.sql(expression, "schema")
2613        return self.func(
2614            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2615        )
2616
2617    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2618        this = self.sql(expression, "this")
2619        kind = self.sql(expression, "kind")
2620        path = self.sql(expression, "path")
2621        path = f" {path}" if path else ""
2622        as_json = " AS JSON" if expression.args.get("as_json") else ""
2623        return f"{this} {kind}{path}{as_json}"
2624
2625    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2626        this = self.sql(expression, "this")
2627        path = self.sql(expression, "path")
2628        path = f", {path}" if path else ""
2629        expressions = self.expressions(expression)
2630        with_ = (
2631            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2632            if expressions
2633            else ""
2634        )
2635        return f"OPENJSON({this}{path}){with_}"
2636
2637    def in_sql(self, expression: exp.In) -> str:
2638        query = expression.args.get("query")
2639        unnest = expression.args.get("unnest")
2640        field = expression.args.get("field")
2641        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2642
2643        if query:
2644            in_sql = self.wrap(self.sql(query))
2645        elif unnest:
2646            in_sql = self.in_unnest_op(unnest)
2647        elif field:
2648            in_sql = self.sql(field)
2649        else:
2650            in_sql = f"({self.expressions(expression, flat=True)})"
2651
2652        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2653
2654    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2655        return f"(SELECT {self.sql(unnest)})"
2656
2657    def interval_sql(self, expression: exp.Interval) -> str:
2658        unit = self.sql(expression, "unit")
2659        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2660            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2661        unit = f" {unit}" if unit else ""
2662
2663        if self.SINGLE_STRING_INTERVAL:
2664            this = expression.this.name if expression.this else ""
2665            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2666
2667        this = self.sql(expression, "this")
2668        if this:
2669            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2670            this = f" {this}" if unwrapped else f" ({this})"
2671
2672        return f"INTERVAL{this}{unit}"
2673
2674    def return_sql(self, expression: exp.Return) -> str:
2675        return f"RETURN {self.sql(expression, 'this')}"
2676
2677    def reference_sql(self, expression: exp.Reference) -> str:
2678        this = self.sql(expression, "this")
2679        expressions = self.expressions(expression, flat=True)
2680        expressions = f"({expressions})" if expressions else ""
2681        options = self.expressions(expression, key="options", flat=True, sep=" ")
2682        options = f" {options}" if options else ""
2683        return f"REFERENCES {this}{expressions}{options}"
2684
2685    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2686        return self.func(self.sql(expression, "this"), *expression.expressions)
2687
2688    def paren_sql(self, expression: exp.Paren) -> str:
2689        if isinstance(expression.unnest(), exp.Select):
2690            sql = self.wrap(expression)
2691        else:
2692            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2693            sql = f"({sql}{self.seg(')', sep='')}"
2694
2695        return self.prepend_ctes(expression, sql)
2696
2697    def neg_sql(self, expression: exp.Neg) -> str:
2698        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2699        this_sql = self.sql(expression, "this")
2700        sep = " " if this_sql[0] == "-" else ""
2701        return f"-{sep}{this_sql}"
2702
2703    def not_sql(self, expression: exp.Not) -> str:
2704        return f"NOT {self.sql(expression, 'this')}"
2705
2706    def alias_sql(self, expression: exp.Alias) -> str:
2707        alias = self.sql(expression, "alias")
2708        alias = f" AS {alias}" if alias else ""
2709        return f"{self.sql(expression, 'this')}{alias}"
2710
2711    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2712        alias = expression.args["alias"]
2713        identifier_alias = isinstance(alias, exp.Identifier)
2714
2715        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2716            alias.replace(exp.Literal.string(alias.output_name))
2717        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2718            alias.replace(exp.to_identifier(alias.output_name))
2719
2720        return self.alias_sql(expression)
2721
2722    def aliases_sql(self, expression: exp.Aliases) -> str:
2723        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
2724
2725    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2726        this = self.sql(expression, "this")
2727        index = self.sql(expression, "expression")
2728        return f"{this} AT {index}"
2729
2730    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2731        this = self.sql(expression, "this")
2732        zone = self.sql(expression, "zone")
2733        return f"{this} AT TIME ZONE {zone}"
2734
2735    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2736        this = self.sql(expression, "this")
2737        zone = self.sql(expression, "zone")
2738        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
2739
2740    def add_sql(self, expression: exp.Add) -> str:
2741        return self.binary(expression, "+")
2742
2743    def and_sql(self, expression: exp.And) -> str:
2744        return self.connector_sql(expression, "AND")
2745
2746    def xor_sql(self, expression: exp.Xor) -> str:
2747        return self.connector_sql(expression, "XOR")
2748
2749    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2750        if not self.pretty:
2751            return self.binary(expression, op)
2752
2753        sqls = tuple(
2754            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2755            for i, e in enumerate(expression.flatten(unnest=False))
2756        )
2757
2758        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2759        return f"{sep}{op} ".join(sqls)
2760
2761    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2762        return self.binary(expression, "&")
2763
2764    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2765        return self.binary(expression, "<<")
2766
2767    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2768        return f"~{self.sql(expression, 'this')}"
2769
2770    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2771        return self.binary(expression, "|")
2772
2773    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2774        return self.binary(expression, ">>")
2775
2776    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2777        return self.binary(expression, "^")
2778
2779    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2780        format_sql = self.sql(expression, "format")
2781        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2782        to_sql = self.sql(expression, "to")
2783        to_sql = f" {to_sql}" if to_sql else ""
2784        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
2785
2786    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2787        zone = self.sql(expression, "this")
2788        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
2789
2790    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2791        return self.func("CURRENT_TIMESTAMP", expression.this)
2792
2793    def collate_sql(self, expression: exp.Collate) -> str:
2794        if self.COLLATE_IS_FUNC:
2795            return self.function_fallback_sql(expression)
2796        return self.binary(expression, "COLLATE")
2797
2798    def command_sql(self, expression: exp.Command) -> str:
2799        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
2800
2801    def comment_sql(self, expression: exp.Comment) -> str:
2802        this = self.sql(expression, "this")
2803        kind = expression.args["kind"]
2804        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2805        expression_sql = self.sql(expression, "expression")
2806        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2807
2808    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2809        this = self.sql(expression, "this")
2810        delete = " DELETE" if expression.args.get("delete") else ""
2811        recompress = self.sql(expression, "recompress")
2812        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2813        to_disk = self.sql(expression, "to_disk")
2814        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2815        to_volume = self.sql(expression, "to_volume")
2816        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2817        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2818
2819    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2820        where = self.sql(expression, "where")
2821        group = self.sql(expression, "group")
2822        aggregates = self.expressions(expression, key="aggregates")
2823        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2824
2825        if not (where or group or aggregates) and len(expression.expressions) == 1:
2826            return f"TTL {self.expressions(expression, flat=True)}"
2827
2828        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2829
2830    def transaction_sql(self, expression: exp.Transaction) -> str:
2831        return "BEGIN"
2832
2833    def commit_sql(self, expression: exp.Commit) -> str:
2834        chain = expression.args.get("chain")
2835        if chain is not None:
2836            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2837
2838        return f"COMMIT{chain or ''}"
2839
2840    def rollback_sql(self, expression: exp.Rollback) -> str:
2841        savepoint = expression.args.get("savepoint")
2842        savepoint = f" TO {savepoint}" if savepoint else ""
2843        return f"ROLLBACK{savepoint}"
2844
2845    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2846        this = self.sql(expression, "this")
2847
2848        dtype = self.sql(expression, "dtype")
2849        if dtype:
2850            collate = self.sql(expression, "collate")
2851            collate = f" COLLATE {collate}" if collate else ""
2852            using = self.sql(expression, "using")
2853            using = f" USING {using}" if using else ""
2854            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2855
2856        default = self.sql(expression, "default")
2857        if default:
2858            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2859
2860        comment = self.sql(expression, "comment")
2861        if comment:
2862            return f"ALTER COLUMN {this} COMMENT {comment}"
2863
2864        if not expression.args.get("drop"):
2865            self.unsupported("Unsupported ALTER COLUMN syntax")
2866
2867        return f"ALTER COLUMN {this} DROP DEFAULT"
2868
2869    def renametable_sql(self, expression: exp.RenameTable) -> str:
2870        if not self.RENAME_TABLE_WITH_DB:
2871            # Remove db from tables
2872            expression = expression.transform(
2873                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2874            )
2875        this = self.sql(expression, "this")
2876        return f"RENAME TO {this}"
2877
2878    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2879        exists = " IF EXISTS" if expression.args.get("exists") else ""
2880        old_column = self.sql(expression, "this")
2881        new_column = self.sql(expression, "to")
2882        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
2883
2884    def altertable_sql(self, expression: exp.AlterTable) -> str:
2885        actions = expression.args["actions"]
2886
2887        if isinstance(actions[0], exp.ColumnDef):
2888            actions = self.add_column_sql(expression)
2889        elif isinstance(actions[0], exp.Schema):
2890            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2891        elif isinstance(actions[0], exp.Delete):
2892            actions = self.expressions(expression, key="actions", flat=True)
2893        else:
2894            actions = self.expressions(expression, key="actions", flat=True)
2895
2896        exists = " IF EXISTS" if expression.args.get("exists") else ""
2897        only = " ONLY" if expression.args.get("only") else ""
2898        options = self.expressions(expression, key="options")
2899        options = f", {options}" if options else ""
2900        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
2901
2902    def add_column_sql(self, expression: exp.AlterTable) -> str:
2903        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2904            return self.expressions(
2905                expression,
2906                key="actions",
2907                prefix="ADD COLUMN ",
2908            )
2909        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
2910
2911    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2912        expressions = self.expressions(expression)
2913        exists = " IF EXISTS " if expression.args.get("exists") else " "
2914        return f"DROP{exists}{expressions}"
2915
2916    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2917        return f"ADD {self.expressions(expression)}"
2918
2919    def distinct_sql(self, expression: exp.Distinct) -> str:
2920        this = self.expressions(expression, flat=True)
2921
2922        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2923            case = exp.case()
2924            for arg in expression.expressions:
2925                case = case.when(arg.is_(exp.null()), exp.null())
2926            this = self.sql(case.else_(f"({this})"))
2927
2928        this = f" {this}" if this else ""
2929
2930        on = self.sql(expression, "on")
2931        on = f" ON {on}" if on else ""
2932        return f"DISTINCT{this}{on}"
2933
2934    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2935        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
2936
2937    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2938        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
2939
2940    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2941        this_sql = self.sql(expression, "this")
2942        expression_sql = self.sql(expression, "expression")
2943        kind = "MAX" if expression.args.get("max") else "MIN"
2944        return f"{this_sql} HAVING {kind} {expression_sql}"
2945
2946    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
2947        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
2948            # The first modifier here will be the one closest to the AggFunc's arg
2949            mods = sorted(
2950                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
2951                key=lambda x: 0
2952                if isinstance(x, exp.HavingMax)
2953                else (1 if isinstance(x, exp.Order) else 2),
2954            )
2955
2956            if mods:
2957                mod = mods[0]
2958                this = expression.__class__(this=mod.this.copy())
2959                this.meta["inline"] = True
2960                mod.this.replace(this)
2961                return self.sql(expression.this)
2962
2963            agg_func = expression.find(exp.AggFunc)
2964
2965            if agg_func:
2966                return self.sql(agg_func)[:-1] + f" {text})"
2967
2968        return f"{self.sql(expression, 'this')} {text}"
2969
2970    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2971        return self.sql(
2972            exp.Cast(
2973                this=exp.Div(this=expression.this, expression=expression.expression),
2974                to=exp.DataType(this=exp.DataType.Type.INT),
2975            )
2976        )
2977
2978    def dpipe_sql(self, expression: exp.DPipe) -> str:
2979        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2980            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2981        return self.binary(expression, "||")
2982
2983    def div_sql(self, expression: exp.Div) -> str:
2984        l, r = expression.left, expression.right
2985
2986        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2987            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2988
2989        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2990            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
2991                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2992
2993        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2994            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2995                return self.sql(
2996                    exp.cast(
2997                        l / r,
2998                        to=exp.DataType.Type.BIGINT,
2999                    )
3000                )
3001
3002        return self.binary(expression, "/")
3003
3004    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3005        return self.binary(expression, "OVERLAPS")
3006
3007    def distance_sql(self, expression: exp.Distance) -> str:
3008        return self.binary(expression, "<->")
3009
3010    def dot_sql(self, expression: exp.Dot) -> str:
3011        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
3012
3013    def eq_sql(self, expression: exp.EQ) -> str:
3014        return self.binary(expression, "=")
3015
3016    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3017        return self.binary(expression, ":=")
3018
3019    def escape_sql(self, expression: exp.Escape) -> str:
3020        return self.binary(expression, "ESCAPE")
3021
3022    def glob_sql(self, expression: exp.Glob) -> str:
3023        return self.binary(expression, "GLOB")
3024
3025    def gt_sql(self, expression: exp.GT) -> str:
3026        return self.binary(expression, ">")
3027
3028    def gte_sql(self, expression: exp.GTE) -> str:
3029        return self.binary(expression, ">=")
3030
3031    def ilike_sql(self, expression: exp.ILike) -> str:
3032        return self.binary(expression, "ILIKE")
3033
3034    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
3035        return self.binary(expression, "ILIKE ANY")
3036
3037    def is_sql(self, expression: exp.Is) -> str:
3038        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3039            return self.sql(
3040                expression.this if expression.expression.this else exp.not_(expression.this)
3041            )
3042        return self.binary(expression, "IS")
3043
3044    def like_sql(self, expression: exp.Like) -> str:
3045        return self.binary(expression, "LIKE")
3046
3047    def likeany_sql(self, expression: exp.LikeAny) -> str:
3048        return self.binary(expression, "LIKE ANY")
3049
3050    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3051        return self.binary(expression, "SIMILAR TO")
3052
3053    def lt_sql(self, expression: exp.LT) -> str:
3054        return self.binary(expression, "<")
3055
3056    def lte_sql(self, expression: exp.LTE) -> str:
3057        return self.binary(expression, "<=")
3058
3059    def mod_sql(self, expression: exp.Mod) -> str:
3060        return self.binary(expression, "%")
3061
3062    def mul_sql(self, expression: exp.Mul) -> str:
3063        return self.binary(expression, "*")
3064
3065    def neq_sql(self, expression: exp.NEQ) -> str:
3066        return self.binary(expression, "<>")
3067
3068    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3069        return self.binary(expression, "IS NOT DISTINCT FROM")
3070
3071    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3072        return self.binary(expression, "IS DISTINCT FROM")
3073
3074    def or_sql(self, expression: exp.Or) -> str:
3075        return self.connector_sql(expression, "OR")
3076
3077    def slice_sql(self, expression: exp.Slice) -> str:
3078        return self.binary(expression, ":")
3079
3080    def sub_sql(self, expression: exp.Sub) -> str:
3081        return self.binary(expression, "-")
3082
3083    def trycast_sql(self, expression: exp.TryCast) -> str:
3084        return self.cast_sql(expression, safe_prefix="TRY_")
3085
3086    def log_sql(self, expression: exp.Log) -> str:
3087        this = expression.this
3088        expr = expression.expression
3089
3090        if not self.dialect.LOG_BASE_FIRST:
3091            this, expr = expr, this
3092
3093        return self.func("LOG", this, expr)
3094
3095    def use_sql(self, expression: exp.Use) -> str:
3096        kind = self.sql(expression, "kind")
3097        kind = f" {kind}" if kind else ""
3098        this = self.sql(expression, "this")
3099        this = f" {this}" if this else ""
3100        return f"USE{kind}{this}"
3101
3102    def binary(self, expression: exp.Binary, op: str) -> str:
3103        op = self.maybe_comment(op, comments=expression.comments)
3104        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
3105
3106    def function_fallback_sql(self, expression: exp.Func) -> str:
3107        args = []
3108
3109        for key in expression.arg_types:
3110            arg_value = expression.args.get(key)
3111
3112            if isinstance(arg_value, list):
3113                for value in arg_value:
3114                    args.append(value)
3115            elif arg_value is not None:
3116                args.append(arg_value)
3117
3118        if self.normalize_functions:
3119            name = expression.sql_name()
3120        else:
3121            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3122
3123        return self.func(name, *args)
3124
3125    def func(
3126        self,
3127        name: str,
3128        *args: t.Optional[exp.Expression | str],
3129        prefix: str = "(",
3130        suffix: str = ")",
3131    ) -> str:
3132        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
3133
3134    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3135        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3136        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3137            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3138        return ", ".join(arg_sqls)
3139
3140    def text_width(self, args: t.Iterable) -> int:
3141        return sum(len(arg) for arg in args)
3142
3143    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3144        return format_time(
3145            self.sql(expression, "format"),
3146            self.dialect.INVERSE_TIME_MAPPING,
3147            self.dialect.INVERSE_TIME_TRIE,
3148        )
3149
3150    def expressions(
3151        self,
3152        expression: t.Optional[exp.Expression] = None,
3153        key: t.Optional[str] = None,
3154        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3155        flat: bool = False,
3156        indent: bool = True,
3157        skip_first: bool = False,
3158        sep: str = ", ",
3159        prefix: str = "",
3160    ) -> str:
3161        expressions = expression.args.get(key or "expressions") if expression else sqls
3162
3163        if not expressions:
3164            return ""
3165
3166        if flat:
3167            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3168
3169        num_sqls = len(expressions)
3170
3171        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3172        pad = " " * self.pad
3173        stripped_sep = sep.strip()
3174
3175        result_sqls = []
3176        for i, e in enumerate(expressions):
3177            sql = self.sql(e, comment=False)
3178            if not sql:
3179                continue
3180
3181            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3182
3183            if self.pretty:
3184                if self.leading_comma:
3185                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3186                else:
3187                    result_sqls.append(
3188                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3189                    )
3190            else:
3191                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3192
3193        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3194        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
3195
3196    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3197        flat = flat or isinstance(expression.parent, exp.Properties)
3198        expressions_sql = self.expressions(expression, flat=flat)
3199        if flat:
3200            return f"{op} {expressions_sql}"
3201        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3202
3203    def naked_property(self, expression: exp.Property) -> str:
3204        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3205        if not property_name:
3206            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3207        return f"{property_name} {self.sql(expression, 'this')}"
3208
3209    def tag_sql(self, expression: exp.Tag) -> str:
3210        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3211
3212    def token_sql(self, token_type: TokenType) -> str:
3213        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3214
3215    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3216        this = self.sql(expression, "this")
3217        expressions = self.no_identify(self.expressions, expression)
3218        expressions = (
3219            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3220        )
3221        return f"{this}{expressions}"
3222
3223    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3224        this = self.sql(expression, "this")
3225        expressions = self.expressions(expression, flat=True)
3226        return f"{this}({expressions})"
3227
3228    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3229        return self.binary(expression, "=>")
3230
3231    def when_sql(self, expression: exp.When) -> str:
3232        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3233        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3234        condition = self.sql(expression, "condition")
3235        condition = f" AND {condition}" if condition else ""
3236
3237        then_expression = expression.args.get("then")
3238        if isinstance(then_expression, exp.Insert):
3239            then = f"INSERT {self.sql(then_expression, 'this')}"
3240            if "expression" in then_expression.args:
3241                then += f" VALUES {self.sql(then_expression, 'expression')}"
3242        elif isinstance(then_expression, exp.Update):
3243            if isinstance(then_expression.args.get("expressions"), exp.Star):
3244                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3245            else:
3246                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3247        else:
3248            then = self.sql(then_expression)
3249        return f"WHEN {matched}{source}{condition} THEN {then}"
3250
3251    def merge_sql(self, expression: exp.Merge) -> str:
3252        table = expression.this
3253        table_alias = ""
3254
3255        hints = table.args.get("hints")
3256        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3257            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3258            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3259
3260        this = self.sql(table)
3261        using = f"USING {self.sql(expression, 'using')}"
3262        on = f"ON {self.sql(expression, 'on')}"
3263        expressions = self.expressions(expression, sep=" ")
3264
3265        return self.prepend_ctes(
3266            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3267        )
3268
3269    def tochar_sql(self, expression: exp.ToChar) -> str:
3270        if expression.args.get("format"):
3271            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3272
3273        return self.sql(exp.cast(expression.this, "text"))
3274
3275    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3276        this = self.sql(expression, "this")
3277        kind = self.sql(expression, "kind")
3278        settings_sql = self.expressions(expression, key="settings", sep=" ")
3279        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3280        return f"{this}({kind}{args})"
3281
3282    def dictrange_sql(self, expression: exp.DictRange) -> str:
3283        this = self.sql(expression, "this")
3284        max = self.sql(expression, "max")
3285        min = self.sql(expression, "min")
3286        return f"{this}(MIN {min} MAX {max})"
3287
3288    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3289        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
3290
3291    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3292        return ""
3293
3294    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3295        expressions = self.expressions(expression, key="expressions", flat=True)
3296        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3297        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3298        buckets = self.sql(expression, "buckets")
3299        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3300
3301    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3302        this = self.sql(expression, "this")
3303        having = self.sql(expression, "having")
3304
3305        if having:
3306            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3307
3308        return self.func("ANY_VALUE", this)
3309
3310    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3311        transform = self.func("TRANSFORM", *expression.expressions)
3312        row_format_before = self.sql(expression, "row_format_before")
3313        row_format_before = f" {row_format_before}" if row_format_before else ""
3314        record_writer = self.sql(expression, "record_writer")
3315        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3316        using = f" USING {self.sql(expression, 'command_script')}"
3317        schema = self.sql(expression, "schema")
3318        schema = f" AS {schema}" if schema else ""
3319        row_format_after = self.sql(expression, "row_format_after")
3320        row_format_after = f" {row_format_after}" if row_format_after else ""
3321        record_reader = self.sql(expression, "record_reader")
3322        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3323        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3324
3325    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3326        key_block_size = self.sql(expression, "key_block_size")
3327        if key_block_size:
3328            return f"KEY_BLOCK_SIZE = {key_block_size}"
3329
3330        using = self.sql(expression, "using")
3331        if using:
3332            return f"USING {using}"
3333
3334        parser = self.sql(expression, "parser")
3335        if parser:
3336            return f"WITH PARSER {parser}"
3337
3338        comment = self.sql(expression, "comment")
3339        if comment:
3340            return f"COMMENT {comment}"
3341
3342        visible = expression.args.get("visible")
3343        if visible is not None:
3344            return "VISIBLE" if visible else "INVISIBLE"
3345
3346        engine_attr = self.sql(expression, "engine_attr")
3347        if engine_attr:
3348            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3349
3350        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3351        if secondary_engine_attr:
3352            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3353
3354        self.unsupported("Unsupported index constraint option.")
3355        return ""
3356
3357    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3358        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3359        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
3360
3361    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3362        kind = self.sql(expression, "kind")
3363        kind = f"{kind} INDEX" if kind else "INDEX"
3364        this = self.sql(expression, "this")
3365        this = f" {this}" if this else ""
3366        index_type = self.sql(expression, "index_type")
3367        index_type = f" USING {index_type}" if index_type else ""
3368        schema = self.sql(expression, "schema")
3369        schema = f" {schema}" if schema else ""
3370        options = self.expressions(expression, key="options", sep=" ")
3371        options = f" {options}" if options else ""
3372        return f"{kind}{this}{index_type}{schema}{options}"
3373
3374    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3375        if self.NVL2_SUPPORTED:
3376            return self.function_fallback_sql(expression)
3377
3378        case = exp.Case().when(
3379            expression.this.is_(exp.null()).not_(copy=False),
3380            expression.args["true"],
3381            copy=False,
3382        )
3383        else_cond = expression.args.get("false")
3384        if else_cond:
3385            case.else_(else_cond, copy=False)
3386
3387        return self.sql(case)
3388
3389    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3390        this = self.sql(expression, "this")
3391        expr = self.sql(expression, "expression")
3392        iterator = self.sql(expression, "iterator")
3393        condition = self.sql(expression, "condition")
3394        condition = f" IF {condition}" if condition else ""
3395        return f"{this} FOR {expr} IN {iterator}{condition}"
3396
3397    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3398        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
3399
3400    def opclass_sql(self, expression: exp.Opclass) -> str:
3401        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
3402
3403    def predict_sql(self, expression: exp.Predict) -> str:
3404        model = self.sql(expression, "this")
3405        model = f"MODEL {model}"
3406        table = self.sql(expression, "expression")
3407        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3408        parameters = self.sql(expression, "params_struct")
3409        return self.func("PREDICT", model, table, parameters or None)
3410
3411    def forin_sql(self, expression: exp.ForIn) -> str:
3412        this = self.sql(expression, "this")
3413        expression_sql = self.sql(expression, "expression")
3414        return f"FOR {this} DO {expression_sql}"
3415
3416    def refresh_sql(self, expression: exp.Refresh) -> str:
3417        this = self.sql(expression, "this")
3418        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3419        return f"REFRESH {table}{this}"
3420
3421    def operator_sql(self, expression: exp.Operator) -> str:
3422        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
3423
3424    def toarray_sql(self, expression: exp.ToArray) -> str:
3425        arg = expression.this
3426        if not arg.type:
3427            from sqlglot.optimizer.annotate_types import annotate_types
3428
3429            arg = annotate_types(arg)
3430
3431        if arg.is_type(exp.DataType.Type.ARRAY):
3432            return self.sql(arg)
3433
3434        cond_for_null = arg.is_(exp.null())
3435        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3436
3437    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3438        this = expression.this
3439        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3440            return self.sql(this)
3441
3442        return self.sql(exp.cast(this, "time"))
3443
3444    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3445        this = expression.this
3446        time_format = self.format_time(expression)
3447
3448        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3449            return self.sql(
3450                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3451            )
3452
3453        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3454            return self.sql(this)
3455
3456        return self.sql(exp.cast(this, "date"))
3457
3458    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3459        return self.sql(
3460            exp.func(
3461                "DATEDIFF",
3462                expression.this,
3463                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3464                "day",
3465            )
3466        )
3467
3468    def lastday_sql(self, expression: exp.LastDay) -> str:
3469        if self.LAST_DAY_SUPPORTS_DATE_PART:
3470            return self.function_fallback_sql(expression)
3471
3472        unit = expression.text("unit")
3473        if unit and unit != "MONTH":
3474            self.unsupported("Date parts are not supported in LAST_DAY.")
3475
3476        return self.func("LAST_DAY", expression.this)
3477
3478    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3479        if self.CAN_IMPLEMENT_ARRAY_ANY:
3480            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3481            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3482            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3483            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3484
3485        from sqlglot.dialects import Dialect
3486
3487        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3488        if self.dialect.__class__ != Dialect:
3489            self.unsupported("ARRAY_ANY is unsupported")
3490
3491        return self.function_fallback_sql(expression)
3492
3493    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
3494        this = expression.this
3495        if isinstance(this, exp.JSONPathWildcard):
3496            this = self.json_path_part(this)
3497            return f".{this}" if this else ""
3498
3499        if exp.SAFE_IDENTIFIER_RE.match(this):
3500            return f".{this}"
3501
3502        this = self.json_path_part(this)
3503        return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}"
3504
3505    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
3506        this = self.json_path_part(expression.this)
3507        return f"[{this}]" if this else ""
3508
3509    def _simplify_unless_literal(self, expression: E) -> E:
3510        if not isinstance(expression, exp.Literal):
3511            from sqlglot.optimizer.simplify import simplify
3512
3513            expression = simplify(expression, dialect=self.dialect)
3514
3515        return expression
3516
3517    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
3518        expression.set("is_end_exclusive", None)
3519        return self.function_fallback_sql(expression)
3520
3521    def struct_sql(self, expression: exp.Struct) -> str:
3522        expression.set(
3523            "expressions",
3524            [
3525                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
3526                for e in expression.expressions
3527            ],
3528        )
3529
3530        return self.function_fallback_sql(expression)
3531
3532    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
3533        low = self.sql(expression, "this")
3534        high = self.sql(expression, "expression")
3535
3536        return f"{low} TO {high}"
3537
3538    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
3539        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
3540        tables = f" {self.expressions(expression)}"
3541
3542        exists = " IF EXISTS" if expression.args.get("exists") else ""
3543
3544        on_cluster = self.sql(expression, "cluster")
3545        on_cluster = f" {on_cluster}" if on_cluster else ""
3546
3547        identity = self.sql(expression, "identity")
3548        identity = f" {identity} IDENTITY" if identity else ""
3549
3550        option = self.sql(expression, "option")
3551        option = f" {option}" if option else ""
3552
3553        partition = self.sql(expression, "partition")
3554        partition = f" {partition}" if partition else ""
3555
3556        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
3557
3558    # This transpiles T-SQL's CONVERT function
3559    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
3560    def convert_sql(self, expression: exp.Convert) -> str:
3561        to = expression.this
3562        value = expression.expression
3563        style = expression.args.get("style")
3564        safe = expression.args.get("safe")
3565        strict = expression.args.get("strict")
3566
3567        if not to or not value:
3568            return ""
3569
3570        # Retrieve length of datatype and override to default if not specified
3571        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
3572            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
3573
3574        transformed: t.Optional[exp.Expression] = None
3575        cast = exp.Cast if strict else exp.TryCast
3576
3577        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
3578        if isinstance(style, exp.Literal) and style.is_int:
3579            from sqlglot.dialects.tsql import TSQL
3580
3581            style_value = style.name
3582            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
3583            if not converted_style:
3584                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
3585
3586            fmt = exp.Literal.string(converted_style)
3587
3588            if to.this == exp.DataType.Type.DATE:
3589                transformed = exp.StrToDate(this=value, format=fmt)
3590            elif to.this == exp.DataType.Type.DATETIME:
3591                transformed = exp.StrToTime(this=value, format=fmt)
3592            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
3593                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
3594            elif to.this == exp.DataType.Type.TEXT:
3595                transformed = exp.TimeToStr(this=value, format=fmt)
3596
3597        if not transformed:
3598            transformed = cast(this=value, to=to, safe=safe)
3599
3600        return self.sql(transformed)
logger = <Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE = re.compile('\\\\(\\d+)')
class Generator:
  37class Generator(metaclass=_Generator):
  38    """
  39    Generator converts a given syntax tree to the corresponding SQL string.
  40
  41    Args:
  42        pretty: Whether to format the produced SQL string.
  43            Default: False.
  44        identify: Determines when an identifier should be quoted. Possible values are:
  45            False (default): Never quote, except in cases where it's mandatory by the dialect.
  46            True or 'always': Always quote.
  47            'safe': Only quote identifiers that are case insensitive.
  48        normalize: Whether to normalize identifiers to lowercase.
  49            Default: False.
  50        pad: The pad size in a formatted string.
  51            Default: 2.
  52        indent: The indentation size in a formatted string.
  53            Default: 2.
  54        normalize_functions: How to normalize function names. Possible values are:
  55            "upper" or True (default): Convert names to uppercase.
  56            "lower": Convert names to lowercase.
  57            False: Disables function name normalization.
  58        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
  59            Default ErrorLevel.WARN.
  60        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
  61            This is only relevant if unsupported_level is ErrorLevel.RAISE.
  62            Default: 3
  63        leading_comma: Whether the comma is leading or trailing in select expressions.
  64            This is only relevant when generating in pretty mode.
  65            Default: False
  66        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
  67            The default is on the smaller end because the length only represents a segment and not the true
  68            line length.
  69            Default: 80
  70        comments: Whether to preserve comments in the output SQL code.
  71            Default: True
  72    """
  73
  74    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
  75        **JSON_PATH_PART_TRANSFORMS,
  76        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
  77        exp.CaseSpecificColumnConstraint: lambda _,
  78        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
  79        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
  80        exp.CharacterSetProperty: lambda self,
  81        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
  82        exp.ClusteredColumnConstraint: lambda self,
  83        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
  84        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
  85        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
  86        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
  87        exp.DateAdd: lambda self, e: self.func(
  88            "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
  89        ),
  90        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
  91        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
  92        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
  93        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
  94        exp.ExternalProperty: lambda *_: "EXTERNAL",
  95        exp.GlobalProperty: lambda *_: "GLOBAL",
  96        exp.HeapProperty: lambda *_: "HEAP",
  97        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
  98        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
  99        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
 100        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
 101        exp.JSONExtract: lambda self, e: self.func(
 102            "JSON_EXTRACT", e.this, e.expression, *e.expressions
 103        ),
 104        exp.JSONExtractScalar: lambda self, e: self.func(
 105            "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions
 106        ),
 107        exp.LanguageProperty: lambda self, e: self.naked_property(e),
 108        exp.LocationProperty: lambda self, e: self.naked_property(e),
 109        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 110        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
 111        exp.NonClusteredColumnConstraint: lambda self,
 112        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 113        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
 114        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
 115        exp.OnCommitProperty: lambda _,
 116        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
 117        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
 118        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
 119        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
 120        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
 121        exp.RemoteWithConnectionModelProperty: lambda self,
 122        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
 123        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
 124        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
 125        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
 126        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
 127        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
 128        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
 129        exp.SqlReadWriteProperty: lambda _, e: e.name,
 130        exp.SqlSecurityProperty: lambda _,
 131        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 132        exp.StabilityProperty: lambda _, e: e.name,
 133        exp.TemporaryProperty: lambda *_: "TEMPORARY",
 134        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
 135        exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression),
 136        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
 137        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
 138        exp.TransientProperty: lambda *_: "TRANSIENT",
 139        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
 140        exp.UnloggedProperty: lambda *_: "UNLOGGED",
 141        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 142        exp.VolatileProperty: lambda *_: "VOLATILE",
 143        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
 144    }
 145
 146    # Whether null ordering is supported in order by
 147    # True: Full Support, None: No support, False: No support in window specifications
 148    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
 149
 150    # Whether ignore nulls is inside the agg or outside.
 151    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
 152    IGNORE_NULLS_IN_FUNC = False
 153
 154    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 155    LOCKING_READS_SUPPORTED = False
 156
 157    # Always do union distinct or union all
 158    EXPLICIT_UNION = False
 159
 160    # Wrap derived values in parens, usually standard but spark doesn't support it
 161    WRAP_DERIVED_VALUES = True
 162
 163    # Whether create function uses an AS before the RETURN
 164    CREATE_FUNCTION_RETURN_AS = True
 165
 166    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
 167    MATCHED_BY_SOURCE = True
 168
 169    # Whether the INTERVAL expression works only with values like '1 day'
 170    SINGLE_STRING_INTERVAL = False
 171
 172    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
 173    INTERVAL_ALLOWS_PLURAL_FORM = True
 174
 175    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
 176    LIMIT_FETCH = "ALL"
 177
 178    # Whether limit and fetch allows expresions or just limits
 179    LIMIT_ONLY_LITERALS = False
 180
 181    # Whether a table is allowed to be renamed with a db
 182    RENAME_TABLE_WITH_DB = True
 183
 184    # The separator for grouping sets and rollups
 185    GROUPINGS_SEP = ","
 186
 187    # The string used for creating an index on a table
 188    INDEX_ON = "ON"
 189
 190    # Whether join hints should be generated
 191    JOIN_HINTS = True
 192
 193    # Whether table hints should be generated
 194    TABLE_HINTS = True
 195
 196    # Whether query hints should be generated
 197    QUERY_HINTS = True
 198
 199    # What kind of separator to use for query hints
 200    QUERY_HINT_SEP = ", "
 201
 202    # Whether comparing against booleans (e.g. x IS TRUE) is supported
 203    IS_BOOL_ALLOWED = True
 204
 205    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
 206    DUPLICATE_KEY_UPDATE_WITH_SET = True
 207
 208    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
 209    LIMIT_IS_TOP = False
 210
 211    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
 212    RETURNING_END = True
 213
 214    # Whether to generate the (+) suffix for columns used in old-style join conditions
 215    COLUMN_JOIN_MARKS_SUPPORTED = False
 216
 217    # Whether to generate an unquoted value for EXTRACT's date part argument
 218    EXTRACT_ALLOWS_QUOTES = True
 219
 220    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
 221    TZ_TO_WITH_TIME_ZONE = False
 222
 223    # Whether the NVL2 function is supported
 224    NVL2_SUPPORTED = True
 225
 226    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
 227    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
 228
 229    # Whether VALUES statements can be used as derived tables.
 230    # MySQL 5 and Redshift do not allow this, so when False, it will convert
 231    # SELECT * VALUES into SELECT UNION
 232    VALUES_AS_TABLE = True
 233
 234    # Whether the word COLUMN is included when adding a column with ALTER TABLE
 235    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
 236
 237    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
 238    UNNEST_WITH_ORDINALITY = True
 239
 240    # Whether FILTER (WHERE cond) can be used for conditional aggregation
 241    AGGREGATE_FILTER_SUPPORTED = True
 242
 243    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
 244    SEMI_ANTI_JOIN_WITH_SIDE = True
 245
 246    # Whether to include the type of a computed column in the CREATE DDL
 247    COMPUTED_COLUMN_WITH_TYPE = True
 248
 249    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
 250    SUPPORTS_TABLE_COPY = True
 251
 252    # Whether parentheses are required around the table sample's expression
 253    TABLESAMPLE_REQUIRES_PARENS = True
 254
 255    # Whether a table sample clause's size needs to be followed by the ROWS keyword
 256    TABLESAMPLE_SIZE_IS_ROWS = True
 257
 258    # The keyword(s) to use when generating a sample clause
 259    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
 260
 261    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
 262    TABLESAMPLE_WITH_METHOD = True
 263
 264    # The keyword to use when specifying the seed of a sample clause
 265    TABLESAMPLE_SEED_KEYWORD = "SEED"
 266
 267    # Whether COLLATE is a function instead of a binary operator
 268    COLLATE_IS_FUNC = False
 269
 270    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
 271    DATA_TYPE_SPECIFIERS_ALLOWED = False
 272
 273    # Whether conditions require booleans WHERE x = 0 vs WHERE x
 274    ENSURE_BOOLS = False
 275
 276    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
 277    CTE_RECURSIVE_KEYWORD_REQUIRED = True
 278
 279    # Whether CONCAT requires >1 arguments
 280    SUPPORTS_SINGLE_ARG_CONCAT = True
 281
 282    # Whether LAST_DAY function supports a date part argument
 283    LAST_DAY_SUPPORTS_DATE_PART = True
 284
 285    # Whether named columns are allowed in table aliases
 286    SUPPORTS_TABLE_ALIAS_COLUMNS = True
 287
 288    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
 289    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
 290
 291    # What delimiter to use for separating JSON key/value pairs
 292    JSON_KEY_VALUE_PAIR_SEP = ":"
 293
 294    # INSERT OVERWRITE TABLE x override
 295    INSERT_OVERWRITE = " OVERWRITE TABLE"
 296
 297    # Whether the SELECT .. INTO syntax is used instead of CTAS
 298    SUPPORTS_SELECT_INTO = False
 299
 300    # Whether UNLOGGED tables can be created
 301    SUPPORTS_UNLOGGED_TABLES = False
 302
 303    # Whether the CREATE TABLE LIKE statement is supported
 304    SUPPORTS_CREATE_TABLE_LIKE = True
 305
 306    # Whether the LikeProperty needs to be specified inside of the schema clause
 307    LIKE_PROPERTY_INSIDE_SCHEMA = False
 308
 309    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
 310    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
 311    MULTI_ARG_DISTINCT = True
 312
 313    # Whether the JSON extraction operators expect a value of type JSON
 314    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
 315
 316    # Whether bracketed keys like ["foo"] are supported in JSON paths
 317    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
 318
 319    # Whether to escape keys using single quotes in JSON paths
 320    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
 321
 322    # The JSONPathPart expressions supported by this dialect
 323    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
 324
 325    # Whether any(f(x) for x in array) can be implemented by this dialect
 326    CAN_IMPLEMENT_ARRAY_ANY = False
 327
 328    TYPE_MAPPING = {
 329        exp.DataType.Type.NCHAR: "CHAR",
 330        exp.DataType.Type.NVARCHAR: "VARCHAR",
 331        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 332        exp.DataType.Type.LONGTEXT: "TEXT",
 333        exp.DataType.Type.TINYTEXT: "TEXT",
 334        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 335        exp.DataType.Type.LONGBLOB: "BLOB",
 336        exp.DataType.Type.TINYBLOB: "BLOB",
 337        exp.DataType.Type.INET: "INET",
 338    }
 339
 340    STAR_MAPPING = {
 341        "except": "EXCEPT",
 342        "replace": "REPLACE",
 343    }
 344
 345    TIME_PART_SINGULARS = {
 346        "MICROSECONDS": "MICROSECOND",
 347        "SECONDS": "SECOND",
 348        "MINUTES": "MINUTE",
 349        "HOURS": "HOUR",
 350        "DAYS": "DAY",
 351        "WEEKS": "WEEK",
 352        "MONTHS": "MONTH",
 353        "QUARTERS": "QUARTER",
 354        "YEARS": "YEAR",
 355    }
 356
 357    AFTER_HAVING_MODIFIER_TRANSFORMS = {
 358        "cluster": lambda self, e: self.sql(e, "cluster"),
 359        "distribute": lambda self, e: self.sql(e, "distribute"),
 360        "qualify": lambda self, e: self.sql(e, "qualify"),
 361        "sort": lambda self, e: self.sql(e, "sort"),
 362        "windows": lambda self, e: (
 363            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
 364            if e.args.get("windows")
 365            else ""
 366        ),
 367    }
 368
 369    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 370
 371    STRUCT_DELIMITER = ("<", ">")
 372
 373    PARAMETER_TOKEN = "@"
 374    NAMED_PLACEHOLDER_TOKEN = ":"
 375
 376    PROPERTIES_LOCATION = {
 377        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 378        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 379        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
 380        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 381        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 382        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 383        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 384        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
 385        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 386        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
 387        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 388        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 389        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
 390        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
 391        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 392        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 393        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 394        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 395        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 396        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 397        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 398        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 399        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
 400        exp.HeapProperty: exp.Properties.Location.POST_WITH,
 401        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
 402        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
 403        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 404        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 405        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 406        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 407        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 408        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
 409        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 410        exp.LogProperty: exp.Properties.Location.POST_NAME,
 411        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 412        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 413        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 414        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 415        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 416        exp.Order: exp.Properties.Location.POST_SCHEMA,
 417        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 418        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 419        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 420        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 421        exp.Property: exp.Properties.Location.POST_WITH,
 422        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 423        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 424        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 425        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 426        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 427        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 428        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 429        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 430        exp.Set: exp.Properties.Location.POST_SCHEMA,
 431        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 432        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 433        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 434        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
 435        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
 436        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 437        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 438        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 439        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 440        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 441        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 442        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 443        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 444        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 445        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
 446        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 447        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 448        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 449        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 450    }
 451
 452    # Keywords that can't be used as unquoted identifier names
 453    RESERVED_KEYWORDS: t.Set[str] = set()
 454
 455    # Expressions whose comments are separated from them for better formatting
 456    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 457        exp.Create,
 458        exp.Delete,
 459        exp.Drop,
 460        exp.From,
 461        exp.Insert,
 462        exp.Join,
 463        exp.Select,
 464        exp.Union,
 465        exp.Update,
 466        exp.Where,
 467        exp.With,
 468    )
 469
 470    # Expressions that should not have their comments generated in maybe_comment
 471    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 472        exp.Binary,
 473        exp.Union,
 474    )
 475
 476    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 477    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 478        exp.Column,
 479        exp.Literal,
 480        exp.Neg,
 481        exp.Paren,
 482    )
 483
 484    PARAMETERIZABLE_TEXT_TYPES = {
 485        exp.DataType.Type.NVARCHAR,
 486        exp.DataType.Type.VARCHAR,
 487        exp.DataType.Type.CHAR,
 488        exp.DataType.Type.NCHAR,
 489    }
 490
 491    # Expressions that need to have all CTEs under them bubbled up to them
 492    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 493
 494    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 495
 496    __slots__ = (
 497        "pretty",
 498        "identify",
 499        "normalize",
 500        "pad",
 501        "_indent",
 502        "normalize_functions",
 503        "unsupported_level",
 504        "max_unsupported",
 505        "leading_comma",
 506        "max_text_width",
 507        "comments",
 508        "dialect",
 509        "unsupported_messages",
 510        "_escaped_quote_end",
 511        "_escaped_identifier_end",
 512    )
 513
 514    def __init__(
 515        self,
 516        pretty: t.Optional[bool] = None,
 517        identify: str | bool = False,
 518        normalize: bool = False,
 519        pad: int = 2,
 520        indent: int = 2,
 521        normalize_functions: t.Optional[str | bool] = None,
 522        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 523        max_unsupported: int = 3,
 524        leading_comma: bool = False,
 525        max_text_width: int = 80,
 526        comments: bool = True,
 527        dialect: DialectType = None,
 528    ):
 529        import sqlglot
 530        from sqlglot.dialects import Dialect
 531
 532        self.pretty = pretty if pretty is not None else sqlglot.pretty
 533        self.identify = identify
 534        self.normalize = normalize
 535        self.pad = pad
 536        self._indent = indent
 537        self.unsupported_level = unsupported_level
 538        self.max_unsupported = max_unsupported
 539        self.leading_comma = leading_comma
 540        self.max_text_width = max_text_width
 541        self.comments = comments
 542        self.dialect = Dialect.get_or_raise(dialect)
 543
 544        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 545        self.normalize_functions = (
 546            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 547        )
 548
 549        self.unsupported_messages: t.List[str] = []
 550        self._escaped_quote_end: str = (
 551            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 552        )
 553        self._escaped_identifier_end: str = (
 554            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
 555        )
 556
 557    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 558        """
 559        Generates the SQL string corresponding to the given syntax tree.
 560
 561        Args:
 562            expression: The syntax tree.
 563            copy: Whether to copy the expression. The generator performs mutations so
 564                it is safer to copy.
 565
 566        Returns:
 567            The SQL string corresponding to `expression`.
 568        """
 569        if copy:
 570            expression = expression.copy()
 571
 572        expression = self.preprocess(expression)
 573
 574        self.unsupported_messages = []
 575        sql = self.sql(expression).strip()
 576
 577        if self.pretty:
 578            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 579
 580        if self.unsupported_level == ErrorLevel.IGNORE:
 581            return sql
 582
 583        if self.unsupported_level == ErrorLevel.WARN:
 584            for msg in self.unsupported_messages:
 585                logger.warning(msg)
 586        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 587            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 588
 589        return sql
 590
 591    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 592        """Apply generic preprocessing transformations to a given expression."""
 593        if (
 594            not expression.parent
 595            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 596            and any(node.parent is not expression for node in expression.find_all(exp.With))
 597        ):
 598            from sqlglot.transforms import move_ctes_to_top_level
 599
 600            expression = move_ctes_to_top_level(expression)
 601
 602        if self.ENSURE_BOOLS:
 603            from sqlglot.transforms import ensure_bools
 604
 605            expression = ensure_bools(expression)
 606
 607        return expression
 608
 609    def unsupported(self, message: str) -> None:
 610        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 611            raise UnsupportedError(message)
 612        self.unsupported_messages.append(message)
 613
 614    def sep(self, sep: str = " ") -> str:
 615        return f"{sep.strip()}\n" if self.pretty else sep
 616
 617    def seg(self, sql: str, sep: str = " ") -> str:
 618        return f"{self.sep(sep)}{sql}"
 619
 620    def pad_comment(self, comment: str) -> str:
 621        comment = " " + comment if comment[0].strip() else comment
 622        comment = comment + " " if comment[-1].strip() else comment
 623        return comment
 624
 625    def maybe_comment(
 626        self,
 627        sql: str,
 628        expression: t.Optional[exp.Expression] = None,
 629        comments: t.Optional[t.List[str]] = None,
 630    ) -> str:
 631        comments = (
 632            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 633            if self.comments
 634            else None
 635        )
 636
 637        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 638            return sql
 639
 640        comments_sql = " ".join(
 641            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 642        )
 643
 644        if not comments_sql:
 645            return sql
 646
 647        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 648            return (
 649                f"{self.sep()}{comments_sql}{sql}"
 650                if sql[0].isspace()
 651                else f"{comments_sql}{self.sep()}{sql}"
 652            )
 653
 654        return f"{sql} {comments_sql}"
 655
 656    def wrap(self, expression: exp.Expression | str) -> str:
 657        this_sql = self.indent(
 658            (
 659                self.sql(expression)
 660                if isinstance(expression, exp.UNWRAPPED_QUERIES)
 661                else self.sql(expression, "this")
 662            ),
 663            level=1,
 664            pad=0,
 665        )
 666        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 667
 668    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 669        original = self.identify
 670        self.identify = False
 671        result = func(*args, **kwargs)
 672        self.identify = original
 673        return result
 674
 675    def normalize_func(self, name: str) -> str:
 676        if self.normalize_functions == "upper" or self.normalize_functions is True:
 677            return name.upper()
 678        if self.normalize_functions == "lower":
 679            return name.lower()
 680        return name
 681
 682    def indent(
 683        self,
 684        sql: str,
 685        level: int = 0,
 686        pad: t.Optional[int] = None,
 687        skip_first: bool = False,
 688        skip_last: bool = False,
 689    ) -> str:
 690        if not self.pretty:
 691            return sql
 692
 693        pad = self.pad if pad is None else pad
 694        lines = sql.split("\n")
 695
 696        return "\n".join(
 697            (
 698                line
 699                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 700                else f"{' ' * (level * self._indent + pad)}{line}"
 701            )
 702            for i, line in enumerate(lines)
 703        )
 704
 705    def sql(
 706        self,
 707        expression: t.Optional[str | exp.Expression],
 708        key: t.Optional[str] = None,
 709        comment: bool = True,
 710    ) -> str:
 711        if not expression:
 712            return ""
 713
 714        if isinstance(expression, str):
 715            return expression
 716
 717        if key:
 718            value = expression.args.get(key)
 719            if value:
 720                return self.sql(value)
 721            return ""
 722
 723        transform = self.TRANSFORMS.get(expression.__class__)
 724
 725        if callable(transform):
 726            sql = transform(self, expression)
 727        elif isinstance(expression, exp.Expression):
 728            exp_handler_name = f"{expression.key}_sql"
 729
 730            if hasattr(self, exp_handler_name):
 731                sql = getattr(self, exp_handler_name)(expression)
 732            elif isinstance(expression, exp.Func):
 733                sql = self.function_fallback_sql(expression)
 734            elif isinstance(expression, exp.Property):
 735                sql = self.property_sql(expression)
 736            else:
 737                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 738        else:
 739            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 740
 741        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 742
 743    def uncache_sql(self, expression: exp.Uncache) -> str:
 744        table = self.sql(expression, "this")
 745        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 746        return f"UNCACHE TABLE{exists_sql} {table}"
 747
 748    def cache_sql(self, expression: exp.Cache) -> str:
 749        lazy = " LAZY" if expression.args.get("lazy") else ""
 750        table = self.sql(expression, "this")
 751        options = expression.args.get("options")
 752        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 753        sql = self.sql(expression, "expression")
 754        sql = f" AS{self.sep()}{sql}" if sql else ""
 755        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 756        return self.prepend_ctes(expression, sql)
 757
 758    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 759        if isinstance(expression.parent, exp.Cast):
 760            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 761        default = "DEFAULT " if expression.args.get("default") else ""
 762        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 763
 764    def column_sql(self, expression: exp.Column) -> str:
 765        join_mark = " (+)" if expression.args.get("join_mark") else ""
 766
 767        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
 768            join_mark = ""
 769            self.unsupported("Outer join syntax using the (+) operator is not supported.")
 770
 771        column = ".".join(
 772            self.sql(part)
 773            for part in (
 774                expression.args.get("catalog"),
 775                expression.args.get("db"),
 776                expression.args.get("table"),
 777                expression.args.get("this"),
 778            )
 779            if part
 780        )
 781
 782        return f"{column}{join_mark}"
 783
 784    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
 785        this = self.sql(expression, "this")
 786        this = f" {this}" if this else ""
 787        position = self.sql(expression, "position")
 788        return f"{position}{this}"
 789
 790    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
 791        column = self.sql(expression, "this")
 792        kind = self.sql(expression, "kind")
 793        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 794        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 795        kind = f"{sep}{kind}" if kind else ""
 796        constraints = f" {constraints}" if constraints else ""
 797        position = self.sql(expression, "position")
 798        position = f" {position}" if position else ""
 799
 800        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
 801            kind = ""
 802
 803        return f"{exists}{column}{kind}{constraints}{position}"
 804
 805    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 806        this = self.sql(expression, "this")
 807        kind_sql = self.sql(expression, "kind").strip()
 808        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 809
 810    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
 811        this = self.sql(expression, "this")
 812        if expression.args.get("not_null"):
 813            persisted = " PERSISTED NOT NULL"
 814        elif expression.args.get("persisted"):
 815            persisted = " PERSISTED"
 816        else:
 817            persisted = ""
 818        return f"AS {this}{persisted}"
 819
 820    def autoincrementcolumnconstraint_sql(self, _) -> str:
 821        return self.token_sql(TokenType.AUTO_INCREMENT)
 822
 823    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
 824        if isinstance(expression.this, list):
 825            this = self.wrap(self.expressions(expression, key="this", flat=True))
 826        else:
 827            this = self.sql(expression, "this")
 828
 829        return f"COMPRESS {this}"
 830
 831    def generatedasidentitycolumnconstraint_sql(
 832        self, expression: exp.GeneratedAsIdentityColumnConstraint
 833    ) -> str:
 834        this = ""
 835        if expression.this is not None:
 836            on_null = " ON NULL" if expression.args.get("on_null") else ""
 837            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
 838
 839        start = expression.args.get("start")
 840        start = f"START WITH {start}" if start else ""
 841        increment = expression.args.get("increment")
 842        increment = f" INCREMENT BY {increment}" if increment else ""
 843        minvalue = expression.args.get("minvalue")
 844        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 845        maxvalue = expression.args.get("maxvalue")
 846        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 847        cycle = expression.args.get("cycle")
 848        cycle_sql = ""
 849
 850        if cycle is not None:
 851            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
 852            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
 853
 854        sequence_opts = ""
 855        if start or increment or cycle_sql:
 856            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
 857            sequence_opts = f" ({sequence_opts.strip()})"
 858
 859        expr = self.sql(expression, "expression")
 860        expr = f"({expr})" if expr else "IDENTITY"
 861
 862        return f"GENERATED{this} AS {expr}{sequence_opts}"
 863
 864    def generatedasrowcolumnconstraint_sql(
 865        self, expression: exp.GeneratedAsRowColumnConstraint
 866    ) -> str:
 867        start = "START" if expression.args.get("start") else "END"
 868        hidden = " HIDDEN" if expression.args.get("hidden") else ""
 869        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
 870
 871    def periodforsystemtimeconstraint_sql(
 872        self, expression: exp.PeriodForSystemTimeConstraint
 873    ) -> str:
 874        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
 875
 876    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
 877        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
 878
 879    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
 880        return f"AS {self.sql(expression, 'this')}"
 881
 882    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
 883        desc = expression.args.get("desc")
 884        if desc is not None:
 885            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
 886        return "PRIMARY KEY"
 887
 888    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
 889        this = self.sql(expression, "this")
 890        this = f" {this}" if this else ""
 891        index_type = expression.args.get("index_type")
 892        index_type = f" USING {index_type}" if index_type else ""
 893        on_conflict = self.sql(expression, "on_conflict")
 894        on_conflict = f" {on_conflict}" if on_conflict else ""
 895        return f"UNIQUE{this}{index_type}{on_conflict}"
 896
 897    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
 898        return self.sql(expression, "this")
 899
 900    def create_sql(self, expression: exp.Create) -> str:
 901        kind = self.sql(expression, "kind")
 902        properties = expression.args.get("properties")
 903        properties_locs = self.locate_properties(properties) if properties else defaultdict()
 904
 905        this = self.createable_sql(expression, properties_locs)
 906
 907        properties_sql = ""
 908        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
 909            exp.Properties.Location.POST_WITH
 910        ):
 911            properties_sql = self.sql(
 912                exp.Properties(
 913                    expressions=[
 914                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
 915                        *properties_locs[exp.Properties.Location.POST_WITH],
 916                    ]
 917                )
 918            )
 919
 920        begin = " BEGIN" if expression.args.get("begin") else ""
 921        end = " END" if expression.args.get("end") else ""
 922
 923        expression_sql = self.sql(expression, "expression")
 924        if expression_sql:
 925            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
 926
 927            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
 928                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
 929                    postalias_props_sql = self.properties(
 930                        exp.Properties(
 931                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
 932                        ),
 933                        wrapped=False,
 934                    )
 935                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
 936                else:
 937                    expression_sql = f" AS{expression_sql}"
 938
 939        postindex_props_sql = ""
 940        if properties_locs.get(exp.Properties.Location.POST_INDEX):
 941            postindex_props_sql = self.properties(
 942                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
 943                wrapped=False,
 944                prefix=" ",
 945            )
 946
 947        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
 948        indexes = f" {indexes}" if indexes else ""
 949        index_sql = indexes + postindex_props_sql
 950
 951        replace = " OR REPLACE" if expression.args.get("replace") else ""
 952        unique = " UNIQUE" if expression.args.get("unique") else ""
 953
 954        postcreate_props_sql = ""
 955        if properties_locs.get(exp.Properties.Location.POST_CREATE):
 956            postcreate_props_sql = self.properties(
 957                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
 958                sep=" ",
 959                prefix=" ",
 960                wrapped=False,
 961            )
 962
 963        modifiers = "".join((replace, unique, postcreate_props_sql))
 964
 965        postexpression_props_sql = ""
 966        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
 967            postexpression_props_sql = self.properties(
 968                exp.Properties(
 969                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
 970                ),
 971                sep=" ",
 972                prefix=" ",
 973                wrapped=False,
 974            )
 975
 976        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
 977        no_schema_binding = (
 978            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
 979        )
 980
 981        clone = self.sql(expression, "clone")
 982        clone = f" {clone}" if clone else ""
 983
 984        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
 985        return self.prepend_ctes(expression, expression_sql)
 986
 987    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
 988        start = self.sql(expression, "start")
 989        start = f"START WITH {start}" if start else ""
 990        increment = self.sql(expression, "increment")
 991        increment = f" INCREMENT BY {increment}" if increment else ""
 992        minvalue = self.sql(expression, "minvalue")
 993        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 994        maxvalue = self.sql(expression, "maxvalue")
 995        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 996        owned = self.sql(expression, "owned")
 997        owned = f" OWNED BY {owned}" if owned else ""
 998
 999        cache = expression.args.get("cache")
1000        if cache is None:
1001            cache_str = ""
1002        elif cache is True:
1003            cache_str = " CACHE"
1004        else:
1005            cache_str = f" CACHE {cache}"
1006
1007        options = self.expressions(expression, key="options", flat=True, sep=" ")
1008        options = f" {options}" if options else ""
1009
1010        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1011
1012    def clone_sql(self, expression: exp.Clone) -> str:
1013        this = self.sql(expression, "this")
1014        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1015        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1016        return f"{shallow}{keyword} {this}"
1017
1018    def describe_sql(self, expression: exp.Describe) -> str:
1019        extended = " EXTENDED" if expression.args.get("extended") else ""
1020        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
1021
1022    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1023        tag = self.sql(expression, "tag")
1024        return f"${tag}${self.sql(expression, 'this')}${tag}$"
1025
1026    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1027        with_ = self.sql(expression, "with")
1028        if with_:
1029            sql = f"{with_}{self.sep()}{sql}"
1030        return sql
1031
1032    def with_sql(self, expression: exp.With) -> str:
1033        sql = self.expressions(expression, flat=True)
1034        recursive = (
1035            "RECURSIVE "
1036            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1037            else ""
1038        )
1039
1040        return f"WITH {recursive}{sql}"
1041
1042    def cte_sql(self, expression: exp.CTE) -> str:
1043        alias = self.sql(expression, "alias")
1044        return f"{alias} AS {self.wrap(expression)}"
1045
1046    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1047        alias = self.sql(expression, "this")
1048        columns = self.expressions(expression, key="columns", flat=True)
1049        columns = f"({columns})" if columns else ""
1050
1051        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1052            columns = ""
1053            self.unsupported("Named columns are not supported in table alias.")
1054
1055        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1056            alias = "_t"
1057
1058        return f"{alias}{columns}"
1059
1060    def bitstring_sql(self, expression: exp.BitString) -> str:
1061        this = self.sql(expression, "this")
1062        if self.dialect.BIT_START:
1063            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1064        return f"{int(this, 2)}"
1065
1066    def hexstring_sql(self, expression: exp.HexString) -> str:
1067        this = self.sql(expression, "this")
1068        if self.dialect.HEX_START:
1069            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1070        return f"{int(this, 16)}"
1071
1072    def bytestring_sql(self, expression: exp.ByteString) -> str:
1073        this = self.sql(expression, "this")
1074        if self.dialect.BYTE_START:
1075            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1076        return this
1077
1078    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1079        this = self.sql(expression, "this")
1080        escape = expression.args.get("escape")
1081
1082        if self.dialect.UNICODE_START:
1083            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
1084            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
1085
1086        if escape:
1087            pattern = re.compile(rf"{escape.name}(\d+)")
1088        else:
1089            pattern = ESCAPED_UNICODE_RE
1090
1091        this = pattern.sub(r"\\u\1", this)
1092        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
1093
1094    def rawstring_sql(self, expression: exp.RawString) -> str:
1095        string = self.escape_str(expression.this.replace("\\", "\\\\"))
1096        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1097
1098    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1099        this = self.sql(expression, "this")
1100        specifier = self.sql(expression, "expression")
1101        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1102        return f"{this}{specifier}"
1103
1104    def datatype_sql(self, expression: exp.DataType) -> str:
1105        type_value = expression.this
1106
1107        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1108            type_sql = self.sql(expression, "kind")
1109        else:
1110            type_sql = (
1111                self.TYPE_MAPPING.get(type_value, type_value.value)
1112                if isinstance(type_value, exp.DataType.Type)
1113                else type_value
1114            )
1115
1116        nested = ""
1117        interior = self.expressions(expression, flat=True)
1118        values = ""
1119
1120        if interior:
1121            if expression.args.get("nested"):
1122                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1123                if expression.args.get("values") is not None:
1124                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1125                    values = self.expressions(expression, key="values", flat=True)
1126                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1127            elif type_value == exp.DataType.Type.INTERVAL:
1128                nested = f" {interior}"
1129            else:
1130                nested = f"({interior})"
1131
1132        type_sql = f"{type_sql}{nested}{values}"
1133        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1134            exp.DataType.Type.TIMETZ,
1135            exp.DataType.Type.TIMESTAMPTZ,
1136        ):
1137            type_sql = f"{type_sql} WITH TIME ZONE"
1138
1139        return type_sql
1140
1141    def directory_sql(self, expression: exp.Directory) -> str:
1142        local = "LOCAL " if expression.args.get("local") else ""
1143        row_format = self.sql(expression, "row_format")
1144        row_format = f" {row_format}" if row_format else ""
1145        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1146
1147    def delete_sql(self, expression: exp.Delete) -> str:
1148        this = self.sql(expression, "this")
1149        this = f" FROM {this}" if this else ""
1150        using = self.sql(expression, "using")
1151        using = f" USING {using}" if using else ""
1152        where = self.sql(expression, "where")
1153        returning = self.sql(expression, "returning")
1154        limit = self.sql(expression, "limit")
1155        tables = self.expressions(expression, key="tables")
1156        tables = f" {tables}" if tables else ""
1157        if self.RETURNING_END:
1158            expression_sql = f"{this}{using}{where}{returning}{limit}"
1159        else:
1160            expression_sql = f"{returning}{this}{using}{where}{limit}"
1161        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1162
1163    def drop_sql(self, expression: exp.Drop) -> str:
1164        this = self.sql(expression, "this")
1165        kind = expression.args["kind"]
1166        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1167        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1168        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1169        cascade = " CASCADE" if expression.args.get("cascade") else ""
1170        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1171        purge = " PURGE" if expression.args.get("purge") else ""
1172        return (
1173            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
1174        )
1175
1176    def except_sql(self, expression: exp.Except) -> str:
1177        return self.set_operations(expression)
1178
1179    def except_op(self, expression: exp.Except) -> str:
1180        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
1181
1182    def fetch_sql(self, expression: exp.Fetch) -> str:
1183        direction = expression.args.get("direction")
1184        direction = f" {direction}" if direction else ""
1185        count = expression.args.get("count")
1186        count = f" {count}" if count else ""
1187        if expression.args.get("percent"):
1188            count = f"{count} PERCENT"
1189        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
1190        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1191
1192    def filter_sql(self, expression: exp.Filter) -> str:
1193        if self.AGGREGATE_FILTER_SUPPORTED:
1194            this = self.sql(expression, "this")
1195            where = self.sql(expression, "expression").strip()
1196            return f"{this} FILTER({where})"
1197
1198        agg = expression.this
1199        agg_arg = agg.this
1200        cond = expression.expression.this
1201        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1202        return self.sql(agg)
1203
1204    def hint_sql(self, expression: exp.Hint) -> str:
1205        if not self.QUERY_HINTS:
1206            self.unsupported("Hints are not supported")
1207            return ""
1208
1209        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1210
1211    def index_sql(self, expression: exp.Index) -> str:
1212        unique = "UNIQUE " if expression.args.get("unique") else ""
1213        primary = "PRIMARY " if expression.args.get("primary") else ""
1214        amp = "AMP " if expression.args.get("amp") else ""
1215        name = self.sql(expression, "this")
1216        name = f"{name} " if name else ""
1217        table = self.sql(expression, "table")
1218        table = f"{self.INDEX_ON} {table}" if table else ""
1219        using = self.sql(expression, "using")
1220        using = f" USING {using}" if using else ""
1221        index = "INDEX " if not table else ""
1222        columns = self.expressions(expression, key="columns", flat=True)
1223        columns = f"({columns})" if columns else ""
1224        partition_by = self.expressions(expression, key="partition_by", flat=True)
1225        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1226        where = self.sql(expression, "where")
1227        include = self.expressions(expression, key="include", flat=True)
1228        if include:
1229            include = f" INCLUDE ({include})"
1230        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"
1231
1232    def identifier_sql(self, expression: exp.Identifier) -> str:
1233        text = expression.name
1234        lower = text.lower()
1235        text = lower if self.normalize and not expression.quoted else text
1236        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
1237        if (
1238            expression.quoted
1239            or self.dialect.can_identify(text, self.identify)
1240            or lower in self.RESERVED_KEYWORDS
1241            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1242        ):
1243            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
1244        return text
1245
1246    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1247        input_format = self.sql(expression, "input_format")
1248        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1249        output_format = self.sql(expression, "output_format")
1250        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1251        return self.sep().join((input_format, output_format))
1252
1253    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1254        string = self.sql(exp.Literal.string(expression.name))
1255        return f"{prefix}{string}"
1256
1257    def partition_sql(self, expression: exp.Partition) -> str:
1258        return f"PARTITION({self.expressions(expression, flat=True)})"
1259
1260    def properties_sql(self, expression: exp.Properties) -> str:
1261        root_properties = []
1262        with_properties = []
1263
1264        for p in expression.expressions:
1265            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1266            if p_loc == exp.Properties.Location.POST_WITH:
1267                with_properties.append(p)
1268            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1269                root_properties.append(p)
1270
1271        return self.root_properties(
1272            exp.Properties(expressions=root_properties)
1273        ) + self.with_properties(exp.Properties(expressions=with_properties))
1274
1275    def root_properties(self, properties: exp.Properties) -> str:
1276        if properties.expressions:
1277            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1278        return ""
1279
1280    def properties(
1281        self,
1282        properties: exp.Properties,
1283        prefix: str = "",
1284        sep: str = ", ",
1285        suffix: str = "",
1286        wrapped: bool = True,
1287    ) -> str:
1288        if properties.expressions:
1289            expressions = self.expressions(properties, sep=sep, indent=False)
1290            if expressions:
1291                expressions = self.wrap(expressions) if wrapped else expressions
1292                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1293        return ""
1294
1295    def with_properties(self, properties: exp.Properties) -> str:
1296        return self.properties(properties, prefix=self.seg("WITH"))
1297
1298    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1299        properties_locs = defaultdict(list)
1300        for p in properties.expressions:
1301            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1302            if p_loc != exp.Properties.Location.UNSUPPORTED:
1303                properties_locs[p_loc].append(p)
1304            else:
1305                self.unsupported(f"Unsupported property {p.key}")
1306
1307        return properties_locs
1308
1309    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1310        if isinstance(expression.this, exp.Dot):
1311            return self.sql(expression, "this")
1312        return f"'{expression.name}'" if string_key else expression.name
1313
1314    def property_sql(self, expression: exp.Property) -> str:
1315        property_cls = expression.__class__
1316        if property_cls == exp.Property:
1317            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1318
1319        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1320        if not property_name:
1321            self.unsupported(f"Unsupported property {expression.key}")
1322
1323        return f"{property_name}={self.sql(expression, 'this')}"
1324
1325    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1326        if self.SUPPORTS_CREATE_TABLE_LIKE:
1327            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1328            options = f" {options}" if options else ""
1329
1330            like = f"LIKE {self.sql(expression, 'this')}{options}"
1331            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1332                like = f"({like})"
1333
1334            return like
1335
1336        if expression.expressions:
1337            self.unsupported("Transpilation of LIKE property options is unsupported")
1338
1339        select = exp.select("*").from_(expression.this).limit(0)
1340        return f"AS {self.sql(select)}"
1341
1342    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1343        no = "NO " if expression.args.get("no") else ""
1344        protection = " PROTECTION" if expression.args.get("protection") else ""
1345        return f"{no}FALLBACK{protection}"
1346
1347    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1348        no = "NO " if expression.args.get("no") else ""
1349        local = expression.args.get("local")
1350        local = f"{local} " if local else ""
1351        dual = "DUAL " if expression.args.get("dual") else ""
1352        before = "BEFORE " if expression.args.get("before") else ""
1353        after = "AFTER " if expression.args.get("after") else ""
1354        return f"{no}{local}{dual}{before}{after}JOURNAL"
1355
1356    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1357        freespace = self.sql(expression, "this")
1358        percent = " PERCENT" if expression.args.get("percent") else ""
1359        return f"FREESPACE={freespace}{percent}"
1360
1361    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1362        if expression.args.get("default"):
1363            property = "DEFAULT"
1364        elif expression.args.get("on"):
1365            property = "ON"
1366        else:
1367            property = "OFF"
1368        return f"CHECKSUM={property}"
1369
1370    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1371        if expression.args.get("no"):
1372            return "NO MERGEBLOCKRATIO"
1373        if expression.args.get("default"):
1374            return "DEFAULT MERGEBLOCKRATIO"
1375
1376        percent = " PERCENT" if expression.args.get("percent") else ""
1377        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1378
1379    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1380        default = expression.args.get("default")
1381        minimum = expression.args.get("minimum")
1382        maximum = expression.args.get("maximum")
1383        if default or minimum or maximum:
1384            if default:
1385                prop = "DEFAULT"
1386            elif minimum:
1387                prop = "MINIMUM"
1388            else:
1389                prop = "MAXIMUM"
1390            return f"{prop} DATABLOCKSIZE"
1391        units = expression.args.get("units")
1392        units = f" {units}" if units else ""
1393        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1394
1395    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1396        autotemp = expression.args.get("autotemp")
1397        always = expression.args.get("always")
1398        default = expression.args.get("default")
1399        manual = expression.args.get("manual")
1400        never = expression.args.get("never")
1401
1402        if autotemp is not None:
1403            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1404        elif always:
1405            prop = "ALWAYS"
1406        elif default:
1407            prop = "DEFAULT"
1408        elif manual:
1409            prop = "MANUAL"
1410        elif never:
1411            prop = "NEVER"
1412        return f"BLOCKCOMPRESSION={prop}"
1413
1414    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1415        no = expression.args.get("no")
1416        no = " NO" if no else ""
1417        concurrent = expression.args.get("concurrent")
1418        concurrent = " CONCURRENT" if concurrent else ""
1419
1420        for_ = ""
1421        if expression.args.get("for_all"):
1422            for_ = " FOR ALL"
1423        elif expression.args.get("for_insert"):
1424            for_ = " FOR INSERT"
1425        elif expression.args.get("for_none"):
1426            for_ = " FOR NONE"
1427        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
1428
1429    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1430        if isinstance(expression.this, list):
1431            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1432        if expression.this:
1433            modulus = self.sql(expression, "this")
1434            remainder = self.sql(expression, "expression")
1435            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1436
1437        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1438        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1439        return f"FROM ({from_expressions}) TO ({to_expressions})"
1440
1441    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1442        this = self.sql(expression, "this")
1443
1444        for_values_or_default = expression.expression
1445        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1446            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1447        else:
1448            for_values_or_default = " DEFAULT"
1449
1450        return f"PARTITION OF {this}{for_values_or_default}"
1451
1452    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1453        kind = expression.args.get("kind")
1454        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1455        for_or_in = expression.args.get("for_or_in")
1456        for_or_in = f" {for_or_in}" if for_or_in else ""
1457        lock_type = expression.args.get("lock_type")
1458        override = " OVERRIDE" if expression.args.get("override") else ""
1459        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1460
1461    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1462        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1463        statistics = expression.args.get("statistics")
1464        statistics_sql = ""
1465        if statistics is not None:
1466            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1467        return f"{data_sql}{statistics_sql}"
1468
1469    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1470        sql = "WITH(SYSTEM_VERSIONING=ON"
1471
1472        if expression.this:
1473            history_table = self.sql(expression, "this")
1474            sql = f"{sql}(HISTORY_TABLE={history_table}"
1475
1476            if expression.expression:
1477                data_consistency_check = self.sql(expression, "expression")
1478                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
1479
1480            sql = f"{sql})"
1481
1482        return f"{sql})"
1483
1484    def insert_sql(self, expression: exp.Insert) -> str:
1485        hint = self.sql(expression, "hint")
1486        overwrite = expression.args.get("overwrite")
1487
1488        if isinstance(expression.this, exp.Directory):
1489            this = " OVERWRITE" if overwrite else " INTO"
1490        else:
1491            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1492
1493        alternative = expression.args.get("alternative")
1494        alternative = f" OR {alternative}" if alternative else ""
1495        ignore = " IGNORE" if expression.args.get("ignore") else ""
1496
1497        this = f"{this} {self.sql(expression, 'this')}"
1498
1499        exists = " IF EXISTS" if expression.args.get("exists") else ""
1500        partition_sql = (
1501            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1502        )
1503        where = self.sql(expression, "where")
1504        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1505        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1506        on_conflict = self.sql(expression, "conflict")
1507        on_conflict = f" {on_conflict}" if on_conflict else ""
1508        by_name = " BY NAME" if expression.args.get("by_name") else ""
1509        returning = self.sql(expression, "returning")
1510
1511        if self.RETURNING_END:
1512            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1513        else:
1514            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1515
1516        sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1517        return self.prepend_ctes(expression, sql)
1518
1519    def intersect_sql(self, expression: exp.Intersect) -> str:
1520        return self.set_operations(expression)
1521
1522    def intersect_op(self, expression: exp.Intersect) -> str:
1523        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
1524
1525    def introducer_sql(self, expression: exp.Introducer) -> str:
1526        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1527
1528    def kill_sql(self, expression: exp.Kill) -> str:
1529        kind = self.sql(expression, "kind")
1530        kind = f" {kind}" if kind else ""
1531        this = self.sql(expression, "this")
1532        this = f" {this}" if this else ""
1533        return f"KILL{kind}{this}"
1534
1535    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1536        return expression.name
1537
1538    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1539        return expression.name
1540
1541    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1542        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1543
1544        constraint = self.sql(expression, "constraint")
1545        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1546
1547        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1548        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1549        action = self.sql(expression, "action")
1550
1551        expressions = self.expressions(expression, flat=True)
1552        if expressions:
1553            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1554            expressions = f" {set_keyword}{expressions}"
1555
1556        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
1557
1558    def returning_sql(self, expression: exp.Returning) -> str:
1559        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1560
1561    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1562        fields = expression.args.get("fields")
1563        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1564        escaped = expression.args.get("escaped")
1565        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1566        items = expression.args.get("collection_items")
1567        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1568        keys = expression.args.get("map_keys")
1569        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1570        lines = expression.args.get("lines")
1571        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1572        null = expression.args.get("null")
1573        null = f" NULL DEFINED AS {null}" if null else ""
1574        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1575
1576    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1577        return f"WITH ({self.expressions(expression, flat=True)})"
1578
1579    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1580        this = f"{self.sql(expression, 'this')} INDEX"
1581        target = self.sql(expression, "target")
1582        target = f" FOR {target}" if target else ""
1583        return f"{this}{target} ({self.expressions(expression, flat=True)})"
1584
1585    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1586        this = self.sql(expression, "this")
1587        kind = self.sql(expression, "kind")
1588        expr = self.sql(expression, "expression")
1589        return f"{this} ({kind} => {expr})"
1590
1591    def table_parts(self, expression: exp.Table) -> str:
1592        return ".".join(
1593            self.sql(part)
1594            for part in (
1595                expression.args.get("catalog"),
1596                expression.args.get("db"),
1597                expression.args.get("this"),
1598            )
1599            if part is not None
1600        )
1601
1602    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1603        table = self.table_parts(expression)
1604        only = "ONLY " if expression.args.get("only") else ""
1605        version = self.sql(expression, "version")
1606        version = f" {version}" if version else ""
1607        alias = self.sql(expression, "alias")
1608        alias = f"{sep}{alias}" if alias else ""
1609        hints = self.expressions(expression, key="hints", sep=" ")
1610        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1611        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1612        pivots = f" {pivots}" if pivots else ""
1613        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1614        laterals = self.expressions(expression, key="laterals", sep="")
1615
1616        file_format = self.sql(expression, "format")
1617        if file_format:
1618            pattern = self.sql(expression, "pattern")
1619            pattern = f", PATTERN => {pattern}" if pattern else ""
1620            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1621
1622        ordinality = expression.args.get("ordinality") or ""
1623        if ordinality:
1624            ordinality = f" WITH ORDINALITY{alias}"
1625            alias = ""
1626
1627        when = self.sql(expression, "when")
1628        if when:
1629            table = f"{table} {when}"
1630
1631        return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
1632
1633    def tablesample_sql(
1634        self,
1635        expression: exp.TableSample,
1636        sep: str = " AS ",
1637        tablesample_keyword: t.Optional[str] = None,
1638    ) -> str:
1639        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1640            table = expression.this.copy()
1641            table.set("alias", None)
1642            this = self.sql(table)
1643            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1644        else:
1645            this = self.sql(expression, "this")
1646            alias = ""
1647
1648        method = self.sql(expression, "method")
1649        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1650        numerator = self.sql(expression, "bucket_numerator")
1651        denominator = self.sql(expression, "bucket_denominator")
1652        field = self.sql(expression, "bucket_field")
1653        field = f" ON {field}" if field else ""
1654        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1655        seed = self.sql(expression, "seed")
1656        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1657
1658        size = self.sql(expression, "size")
1659        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1660            size = f"{size} ROWS"
1661
1662        percent = self.sql(expression, "percent")
1663        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1664            percent = f"{percent} PERCENT"
1665
1666        expr = f"{bucket}{percent}{size}"
1667        if self.TABLESAMPLE_REQUIRES_PARENS:
1668            expr = f"({expr})"
1669
1670        return (
1671            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1672        )
1673
1674    def pivot_sql(self, expression: exp.Pivot) -> str:
1675        expressions = self.expressions(expression, flat=True)
1676
1677        if expression.this:
1678            this = self.sql(expression, "this")
1679            if not expressions:
1680                return f"UNPIVOT {this}"
1681
1682            on = f"{self.seg('ON')} {expressions}"
1683            using = self.expressions(expression, key="using", flat=True)
1684            using = f"{self.seg('USING')} {using}" if using else ""
1685            group = self.sql(expression, "group")
1686            return f"PIVOT {this}{on}{using}{group}"
1687
1688        alias = self.sql(expression, "alias")
1689        alias = f" AS {alias}" if alias else ""
1690        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1691        field = self.sql(expression, "field")
1692        include_nulls = expression.args.get("include_nulls")
1693        if include_nulls is not None:
1694            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1695        else:
1696            nulls = ""
1697        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1698
1699    def version_sql(self, expression: exp.Version) -> str:
1700        this = f"FOR {expression.name}"
1701        kind = expression.text("kind")
1702        expr = self.sql(expression, "expression")
1703        return f"{this} {kind} {expr}"
1704
1705    def tuple_sql(self, expression: exp.Tuple) -> str:
1706        return f"({self.expressions(expression, flat=True)})"
1707
1708    def update_sql(self, expression: exp.Update) -> str:
1709        this = self.sql(expression, "this")
1710        set_sql = self.expressions(expression, flat=True)
1711        from_sql = self.sql(expression, "from")
1712        where_sql = self.sql(expression, "where")
1713        returning = self.sql(expression, "returning")
1714        order = self.sql(expression, "order")
1715        limit = self.sql(expression, "limit")
1716        if self.RETURNING_END:
1717            expression_sql = f"{from_sql}{where_sql}{returning}"
1718        else:
1719            expression_sql = f"{returning}{from_sql}{where_sql}"
1720        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1721        return self.prepend_ctes(expression, sql)
1722
1723    def values_sql(self, expression: exp.Values) -> str:
1724        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1725        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1726            args = self.expressions(expression)
1727            alias = self.sql(expression, "alias")
1728            values = f"VALUES{self.seg('')}{args}"
1729            values = (
1730                f"({values})"
1731                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1732                else values
1733            )
1734            return f"{values} AS {alias}" if alias else values
1735
1736        # Converts `VALUES...` expression into a series of select unions.
1737        alias_node = expression.args.get("alias")
1738        column_names = alias_node and alias_node.columns
1739
1740        selects: t.List[exp.Query] = []
1741
1742        for i, tup in enumerate(expression.expressions):
1743            row = tup.expressions
1744
1745            if i == 0 and column_names:
1746                row = [
1747                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1748                ]
1749
1750            selects.append(exp.Select(expressions=row))
1751
1752        if self.pretty:
1753            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1754            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1755            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1756            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1757            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
1758
1759        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
1760        unions = " UNION ALL ".join(self.sql(select) for select in selects)
1761        return f"({unions}){alias}"
1762
1763    def var_sql(self, expression: exp.Var) -> str:
1764        return self.sql(expression, "this")
1765
1766    def into_sql(self, expression: exp.Into) -> str:
1767        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1768        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1769        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
1770
1771    def from_sql(self, expression: exp.From) -> str:
1772        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
1773
1774    def group_sql(self, expression: exp.Group) -> str:
1775        group_by = self.op_expressions("GROUP BY", expression)
1776
1777        if expression.args.get("all"):
1778            return f"{group_by} ALL"
1779
1780        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1781        grouping_sets = (
1782            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1783        )
1784
1785        cube = expression.args.get("cube", [])
1786        if seq_get(cube, 0) is True:
1787            return f"{group_by}{self.seg('WITH CUBE')}"
1788        else:
1789            cube_sql = self.expressions(expression, key="cube", indent=False)
1790            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1791
1792        rollup = expression.args.get("rollup", [])
1793        if seq_get(rollup, 0) is True:
1794            return f"{group_by}{self.seg('WITH ROLLUP')}"
1795        else:
1796            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1797            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1798
1799        groupings = csv(
1800            grouping_sets,
1801            cube_sql,
1802            rollup_sql,
1803            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
1804            sep=self.GROUPINGS_SEP,
1805        )
1806
1807        if expression.args.get("expressions") and groupings:
1808            group_by = f"{group_by}{self.GROUPINGS_SEP}"
1809
1810        return f"{group_by}{groupings}"
1811
1812    def having_sql(self, expression: exp.Having) -> str:
1813        this = self.indent(self.sql(expression, "this"))
1814        return f"{self.seg('HAVING')}{self.sep()}{this}"
1815
1816    def connect_sql(self, expression: exp.Connect) -> str:
1817        start = self.sql(expression, "start")
1818        start = self.seg(f"START WITH {start}") if start else ""
1819        connect = self.sql(expression, "connect")
1820        connect = self.seg(f"CONNECT BY {connect}")
1821        return start + connect
1822
1823    def prior_sql(self, expression: exp.Prior) -> str:
1824        return f"PRIOR {self.sql(expression, 'this')}"
1825
1826    def join_sql(self, expression: exp.Join) -> str:
1827        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
1828            side = None
1829        else:
1830            side = expression.side
1831
1832        op_sql = " ".join(
1833            op
1834            for op in (
1835                expression.method,
1836                "GLOBAL" if expression.args.get("global") else None,
1837                side,
1838                expression.kind,
1839                expression.hint if self.JOIN_HINTS else None,
1840            )
1841            if op
1842        )
1843        on_sql = self.sql(expression, "on")
1844        using = expression.args.get("using")
1845
1846        if not on_sql and using:
1847            on_sql = csv(*(self.sql(column) for column in using))
1848
1849        this = expression.this
1850        this_sql = self.sql(this)
1851
1852        if on_sql:
1853            on_sql = self.indent(on_sql, skip_first=True)
1854            space = self.seg(" " * self.pad) if self.pretty else " "
1855            if using:
1856                on_sql = f"{space}USING ({on_sql})"
1857            else:
1858                on_sql = f"{space}ON {on_sql}"
1859        elif not op_sql:
1860            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
1861                return f" {this_sql}"
1862
1863            return f", {this_sql}"
1864
1865        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
1866        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
1867
1868    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1869        args = self.expressions(expression, flat=True)
1870        args = f"({args})" if len(args.split(",")) > 1 else args
1871        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
1872
1873    def lateral_op(self, expression: exp.Lateral) -> str:
1874        cross_apply = expression.args.get("cross_apply")
1875
1876        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
1877        if cross_apply is True:
1878            op = "INNER JOIN "
1879        elif cross_apply is False:
1880            op = "LEFT JOIN "
1881        else:
1882            op = ""
1883
1884        return f"{op}LATERAL"
1885
1886    def lateral_sql(self, expression: exp.Lateral) -> str:
1887        this = self.sql(expression, "this")
1888
1889        if expression.args.get("view"):
1890            alias = expression.args["alias"]
1891            columns = self.expressions(alias, key="columns", flat=True)
1892            table = f" {alias.name}" if alias.name else ""
1893            columns = f" AS {columns}" if columns else ""
1894            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1895            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1896
1897        alias = self.sql(expression, "alias")
1898        alias = f" AS {alias}" if alias else ""
1899        return f"{self.lateral_op(expression)} {this}{alias}"
1900
1901    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
1902        this = self.sql(expression, "this")
1903
1904        args = [
1905            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
1906            for e in (expression.args.get(k) for k in ("offset", "expression"))
1907            if e
1908        ]
1909
1910        args_sql = ", ".join(self.sql(e) for e in args)
1911        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
1912        expressions = self.expressions(expression, flat=True)
1913        expressions = f" BY {expressions}" if expressions else ""
1914
1915        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
1916
1917    def offset_sql(self, expression: exp.Offset) -> str:
1918        this = self.sql(expression, "this")
1919        value = expression.expression
1920        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
1921        expressions = self.expressions(expression, flat=True)
1922        expressions = f" BY {expressions}" if expressions else ""
1923        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
1924
1925    def setitem_sql(self, expression: exp.SetItem) -> str:
1926        kind = self.sql(expression, "kind")
1927        kind = f"{kind} " if kind else ""
1928        this = self.sql(expression, "this")
1929        expressions = self.expressions(expression)
1930        collate = self.sql(expression, "collate")
1931        collate = f" COLLATE {collate}" if collate else ""
1932        global_ = "GLOBAL " if expression.args.get("global") else ""
1933        return f"{global_}{kind}{this}{expressions}{collate}"
1934
1935    def set_sql(self, expression: exp.Set) -> str:
1936        expressions = (
1937            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
1938        )
1939        tag = " TAG" if expression.args.get("tag") else ""
1940        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
1941
1942    def pragma_sql(self, expression: exp.Pragma) -> str:
1943        return f"PRAGMA {self.sql(expression, 'this')}"
1944
1945    def lock_sql(self, expression: exp.Lock) -> str:
1946        if not self.LOCKING_READS_SUPPORTED:
1947            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1948            return ""
1949
1950        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
1951        expressions = self.expressions(expression, flat=True)
1952        expressions = f" OF {expressions}" if expressions else ""
1953        wait = expression.args.get("wait")
1954
1955        if wait is not None:
1956            if isinstance(wait, exp.Literal):
1957                wait = f" WAIT {self.sql(wait)}"
1958            else:
1959                wait = " NOWAIT" if wait else " SKIP LOCKED"
1960
1961        return f"{lock_type}{expressions}{wait or ''}"
1962
1963    def literal_sql(self, expression: exp.Literal) -> str:
1964        text = expression.this or ""
1965        if expression.is_string:
1966            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
1967        return text
1968
1969    def escape_str(self, text: str) -> str:
1970        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
1971        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
1972            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
1973        elif self.pretty:
1974            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1975        return text
1976
1977    def loaddata_sql(self, expression: exp.LoadData) -> str:
1978        local = " LOCAL" if expression.args.get("local") else ""
1979        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1980        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1981        this = f" INTO TABLE {self.sql(expression, 'this')}"
1982        partition = self.sql(expression, "partition")
1983        partition = f" {partition}" if partition else ""
1984        input_format = self.sql(expression, "input_format")
1985        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1986        serde = self.sql(expression, "serde")
1987        serde = f" SERDE {serde}" if serde else ""
1988        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
1989
1990    def null_sql(self, *_) -> str:
1991        return "NULL"
1992
1993    def boolean_sql(self, expression: exp.Boolean) -> str:
1994        return "TRUE" if expression.this else "FALSE"
1995
1996    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1997        this = self.sql(expression, "this")
1998        this = f"{this} " if this else this
1999        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2000        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2001        interpolated_values = [
2002            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
2003            for named_expression in expression.args.get("interpolate") or []
2004        ]
2005        interpolate = (
2006            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2007        )
2008        return f"{order}{interpolate}"
2009
2010    def withfill_sql(self, expression: exp.WithFill) -> str:
2011        from_sql = self.sql(expression, "from")
2012        from_sql = f" FROM {from_sql}" if from_sql else ""
2013        to_sql = self.sql(expression, "to")
2014        to_sql = f" TO {to_sql}" if to_sql else ""
2015        step_sql = self.sql(expression, "step")
2016        step_sql = f" STEP {step_sql}" if step_sql else ""
2017        return f"WITH FILL{from_sql}{to_sql}{step_sql}"
2018
2019    def cluster_sql(self, expression: exp.Cluster) -> str:
2020        return self.op_expressions("CLUSTER BY", expression)
2021
2022    def distribute_sql(self, expression: exp.Distribute) -> str:
2023        return self.op_expressions("DISTRIBUTE BY", expression)
2024
2025    def sort_sql(self, expression: exp.Sort) -> str:
2026        return self.op_expressions("SORT BY", expression)
2027
2028    def ordered_sql(self, expression: exp.Ordered) -> str:
2029        desc = expression.args.get("desc")
2030        asc = not desc
2031
2032        nulls_first = expression.args.get("nulls_first")
2033        nulls_last = not nulls_first
2034        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2035        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2036        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2037
2038        this = self.sql(expression, "this")
2039
2040        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2041        nulls_sort_change = ""
2042        if nulls_first and (
2043            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2044        ):
2045            nulls_sort_change = " NULLS FIRST"
2046        elif (
2047            nulls_last
2048            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2049            and not nulls_are_last
2050        ):
2051            nulls_sort_change = " NULLS LAST"
2052
2053        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2054        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2055            window = expression.find_ancestor(exp.Window, exp.Select)
2056            if isinstance(window, exp.Window) and window.args.get("spec"):
2057                self.unsupported(
2058                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2059                )
2060                nulls_sort_change = ""
2061            elif self.NULL_ORDERING_SUPPORTED is None:
2062                if expression.this.is_int:
2063                    self.unsupported(
2064                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2065                    )
2066                else:
2067                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2068                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2069                nulls_sort_change = ""
2070
2071        with_fill = self.sql(expression, "with_fill")
2072        with_fill = f" {with_fill}" if with_fill else ""
2073
2074        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2075
2076    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2077        partition = self.partition_by_sql(expression)
2078        order = self.sql(expression, "order")
2079        measures = self.expressions(expression, key="measures")
2080        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2081        rows = self.sql(expression, "rows")
2082        rows = self.seg(rows) if rows else ""
2083        after = self.sql(expression, "after")
2084        after = self.seg(after) if after else ""
2085        pattern = self.sql(expression, "pattern")
2086        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2087        definition_sqls = [
2088            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2089            for definition in expression.args.get("define", [])
2090        ]
2091        definitions = self.expressions(sqls=definition_sqls)
2092        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2093        body = "".join(
2094            (
2095                partition,
2096                order,
2097                measures,
2098                rows,
2099                after,
2100                pattern,
2101                define,
2102            )
2103        )
2104        alias = self.sql(expression, "alias")
2105        alias = f" {alias}" if alias else ""
2106        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2107
2108    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2109        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
2110
2111        # If the limit is generated as TOP, we need to ensure it's not generated twice
2112        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
2113
2114        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2115            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2116        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2117            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2118
2119        fetch = isinstance(limit, exp.Fetch)
2120
2121        offset_limit_modifiers = (
2122            self.offset_limit_modifiers(expression, fetch, limit)
2123            if with_offset_limit_modifiers
2124            else []
2125        )
2126
2127        options = self.expressions(expression, key="options")
2128        if options:
2129            options = f" OPTION{self.wrap(options)}"
2130
2131        return csv(
2132            *sqls,
2133            *[self.sql(join) for join in expression.args.get("joins") or []],
2134            self.sql(expression, "connect"),
2135            self.sql(expression, "match"),
2136            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2137            self.sql(expression, "prewhere"),
2138            self.sql(expression, "where"),
2139            self.sql(expression, "group"),
2140            self.sql(expression, "having"),
2141            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2142            self.sql(expression, "order"),
2143            *offset_limit_modifiers,
2144            *self.after_limit_modifiers(expression),
2145            options,
2146            sep="",
2147        )
2148
2149    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2150        return ""
2151
2152    def offset_limit_modifiers(
2153        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2154    ) -> t.List[str]:
2155        return [
2156            self.sql(expression, "offset") if fetch else self.sql(limit),
2157            self.sql(limit) if fetch else self.sql(expression, "offset"),
2158        ]
2159
2160    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2161        locks = self.expressions(expression, key="locks", sep=" ")
2162        locks = f" {locks}" if locks else ""
2163        return [locks, self.sql(expression, "sample")]
2164
2165    def select_sql(self, expression: exp.Select) -> str:
2166        into = expression.args.get("into")
2167        if not self.SUPPORTS_SELECT_INTO and into:
2168            into.pop()
2169
2170        hint = self.sql(expression, "hint")
2171        distinct = self.sql(expression, "distinct")
2172        distinct = f" {distinct}" if distinct else ""
2173        kind = self.sql(expression, "kind")
2174        limit = expression.args.get("limit")
2175        top = (
2176            self.limit_sql(limit, top=True)
2177            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2178            else ""
2179        )
2180
2181        expressions = self.expressions(expression)
2182
2183        if kind:
2184            if kind in self.SELECT_KINDS:
2185                kind = f" AS {kind}"
2186            else:
2187                if kind == "STRUCT":
2188                    expressions = self.expressions(
2189                        sqls=[
2190                            self.sql(
2191                                exp.Struct(
2192                                    expressions=[
2193                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2194                                        if isinstance(e, exp.Alias)
2195                                        else e
2196                                        for e in expression.expressions
2197                                    ]
2198                                )
2199                            )
2200                        ]
2201                    )
2202                kind = ""
2203
2204        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2205        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2206        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2207        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2208        sql = self.query_modifiers(
2209            expression,
2210            f"SELECT{top_distinct}{kind}{expressions}",
2211            self.sql(expression, "into", comment=False),
2212            self.sql(expression, "from", comment=False),
2213        )
2214
2215        sql = self.prepend_ctes(expression, sql)
2216
2217        if not self.SUPPORTS_SELECT_INTO and into:
2218            if into.args.get("temporary"):
2219                table_kind = " TEMPORARY"
2220            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2221                table_kind = " UNLOGGED"
2222            else:
2223                table_kind = ""
2224            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2225
2226        return sql
2227
2228    def schema_sql(self, expression: exp.Schema) -> str:
2229        this = self.sql(expression, "this")
2230        sql = self.schema_columns_sql(expression)
2231        return f"{this} {sql}" if this and sql else this or sql
2232
2233    def schema_columns_sql(self, expression: exp.Schema) -> str:
2234        if expression.expressions:
2235            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2236        return ""
2237
2238    def star_sql(self, expression: exp.Star) -> str:
2239        except_ = self.expressions(expression, key="except", flat=True)
2240        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2241        replace = self.expressions(expression, key="replace", flat=True)
2242        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2243        return f"*{except_}{replace}"
2244
2245    def parameter_sql(self, expression: exp.Parameter) -> str:
2246        this = self.sql(expression, "this")
2247        return f"{self.PARAMETER_TOKEN}{this}"
2248
2249    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2250        this = self.sql(expression, "this")
2251        kind = expression.text("kind")
2252        if kind:
2253            kind = f"{kind}."
2254        return f"@@{kind}{this}"
2255
2256    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2257        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
2258
2259    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2260        alias = self.sql(expression, "alias")
2261        alias = f"{sep}{alias}" if alias else ""
2262
2263        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2264        pivots = f" {pivots}" if pivots else ""
2265
2266        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2267        return self.prepend_ctes(expression, sql)
2268
2269    def qualify_sql(self, expression: exp.Qualify) -> str:
2270        this = self.indent(self.sql(expression, "this"))
2271        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2272
2273    def set_operations(self, expression: exp.Union) -> str:
2274        sqls: t.List[str] = []
2275        stack: t.List[t.Union[str, exp.Expression]] = [expression]
2276
2277        while stack:
2278            node = stack.pop()
2279
2280            if isinstance(node, exp.Union):
2281                stack.append(node.expression)
2282                stack.append(
2283                    self.maybe_comment(
2284                        getattr(self, f"{node.key}_op")(node),
2285                        expression=node.this,
2286                        comments=node.comments,
2287                    )
2288                )
2289                stack.append(node.this)
2290            else:
2291                sqls.append(self.sql(node))
2292
2293        this = self.sep().join(sqls)
2294        this = self.query_modifiers(expression, this)
2295        return self.prepend_ctes(expression, this)
2296
2297    def union_sql(self, expression: exp.Union) -> str:
2298        return self.set_operations(expression)
2299
2300    def union_op(self, expression: exp.Union) -> str:
2301        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2302        kind = kind if expression.args.get("distinct") else " ALL"
2303        by_name = " BY NAME" if expression.args.get("by_name") else ""
2304        return f"UNION{kind}{by_name}"
2305
2306    def unnest_sql(self, expression: exp.Unnest) -> str:
2307        args = self.expressions(expression, flat=True)
2308
2309        alias = expression.args.get("alias")
2310        offset = expression.args.get("offset")
2311
2312        if self.UNNEST_WITH_ORDINALITY:
2313            if alias and isinstance(offset, exp.Expression):
2314                alias.append("columns", offset)
2315
2316        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2317            columns = alias.columns
2318            alias = self.sql(columns[0]) if columns else ""
2319        else:
2320            alias = self.sql(alias)
2321
2322        alias = f" AS {alias}" if alias else alias
2323        if self.UNNEST_WITH_ORDINALITY:
2324            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2325        else:
2326            if isinstance(offset, exp.Expression):
2327                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2328            elif offset:
2329                suffix = f"{alias} WITH OFFSET"
2330            else:
2331                suffix = alias
2332
2333        return f"UNNEST({args}){suffix}"
2334
2335    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2336        return ""
2337
2338    def where_sql(self, expression: exp.Where) -> str:
2339        this = self.indent(self.sql(expression, "this"))
2340        return f"{self.seg('WHERE')}{self.sep()}{this}"
2341
2342    def window_sql(self, expression: exp.Window) -> str:
2343        this = self.sql(expression, "this")
2344        partition = self.partition_by_sql(expression)
2345        order = expression.args.get("order")
2346        order = self.order_sql(order, flat=True) if order else ""
2347        spec = self.sql(expression, "spec")
2348        alias = self.sql(expression, "alias")
2349        over = self.sql(expression, "over") or "OVER"
2350
2351        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2352
2353        first = expression.args.get("first")
2354        if first is None:
2355            first = ""
2356        else:
2357            first = "FIRST" if first else "LAST"
2358
2359        if not partition and not order and not spec and alias:
2360            return f"{this} {alias}"
2361
2362        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2363        return f"{this} ({args})"
2364
2365    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2366        partition = self.expressions(expression, key="partition_by", flat=True)
2367        return f"PARTITION BY {partition}" if partition else ""
2368
2369    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2370        kind = self.sql(expression, "kind")
2371        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2372        end = (
2373            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2374            or "CURRENT ROW"
2375        )
2376        return f"{kind} BETWEEN {start} AND {end}"
2377
2378    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2379        this = self.sql(expression, "this")
2380        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2381        return f"{this} WITHIN GROUP ({expression_sql})"
2382
2383    def between_sql(self, expression: exp.Between) -> str:
2384        this = self.sql(expression, "this")
2385        low = self.sql(expression, "low")
2386        high = self.sql(expression, "high")
2387        return f"{this} BETWEEN {low} AND {high}"
2388
2389    def bracket_sql(self, expression: exp.Bracket) -> str:
2390        expressions = apply_index_offset(
2391            expression.this,
2392            expression.expressions,
2393            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2394        )
2395        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2396        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2397
2398    def all_sql(self, expression: exp.All) -> str:
2399        return f"ALL {self.wrap(expression)}"
2400
2401    def any_sql(self, expression: exp.Any) -> str:
2402        this = self.sql(expression, "this")
2403        if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2404            this = self.wrap(this)
2405        return f"ANY {this}"
2406
2407    def exists_sql(self, expression: exp.Exists) -> str:
2408        return f"EXISTS{self.wrap(expression)}"
2409
2410    def case_sql(self, expression: exp.Case) -> str:
2411        this = self.sql(expression, "this")
2412        statements = [f"CASE {this}" if this else "CASE"]
2413
2414        for e in expression.args["ifs"]:
2415            statements.append(f"WHEN {self.sql(e, 'this')}")
2416            statements.append(f"THEN {self.sql(e, 'true')}")
2417
2418        default = self.sql(expression, "default")
2419
2420        if default:
2421            statements.append(f"ELSE {default}")
2422
2423        statements.append("END")
2424
2425        if self.pretty and self.text_width(statements) > self.max_text_width:
2426            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2427
2428        return " ".join(statements)
2429
2430    def constraint_sql(self, expression: exp.Constraint) -> str:
2431        this = self.sql(expression, "this")
2432        expressions = self.expressions(expression, flat=True)
2433        return f"CONSTRAINT {this} {expressions}"
2434
2435    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2436        order = expression.args.get("order")
2437        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2438        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2439
2440    def extract_sql(self, expression: exp.Extract) -> str:
2441        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2442        expression_sql = self.sql(expression, "expression")
2443        return f"EXTRACT({this} FROM {expression_sql})"
2444
2445    def trim_sql(self, expression: exp.Trim) -> str:
2446        trim_type = self.sql(expression, "position")
2447
2448        if trim_type == "LEADING":
2449            return self.func("LTRIM", expression.this)
2450        elif trim_type == "TRAILING":
2451            return self.func("RTRIM", expression.this)
2452        else:
2453            return self.func("TRIM", expression.this, expression.expression)
2454
2455    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2456        args = expression.expressions
2457        if isinstance(expression, exp.ConcatWs):
2458            args = args[1:]  # Skip the delimiter
2459
2460        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2461            args = [exp.cast(e, "text") for e in args]
2462
2463        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2464            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2465
2466        return args
2467
2468    def concat_sql(self, expression: exp.Concat) -> str:
2469        expressions = self.convert_concat_args(expression)
2470
2471        # Some dialects don't allow a single-argument CONCAT call
2472        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2473            return self.sql(expressions[0])
2474
2475        return self.func("CONCAT", *expressions)
2476
2477    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2478        return self.func(
2479            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2480        )
2481
2482    def check_sql(self, expression: exp.Check) -> str:
2483        this = self.sql(expression, key="this")
2484        return f"CHECK ({this})"
2485
2486    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2487        expressions = self.expressions(expression, flat=True)
2488        reference = self.sql(expression, "reference")
2489        reference = f" {reference}" if reference else ""
2490        delete = self.sql(expression, "delete")
2491        delete = f" ON DELETE {delete}" if delete else ""
2492        update = self.sql(expression, "update")
2493        update = f" ON UPDATE {update}" if update else ""
2494        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2495
2496    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2497        expressions = self.expressions(expression, flat=True)
2498        options = self.expressions(expression, key="options", flat=True, sep=" ")
2499        options = f" {options}" if options else ""
2500        return f"PRIMARY KEY ({expressions}){options}"
2501
2502    def if_sql(self, expression: exp.If) -> str:
2503        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
2504
2505    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2506        modifier = expression.args.get("modifier")
2507        modifier = f" {modifier}" if modifier else ""
2508        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
2509
2510    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2511        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
2512
2513    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2514        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2515        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2516
2517    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2518        if isinstance(expression, exp.JSONPathPart):
2519            transform = self.TRANSFORMS.get(expression.__class__)
2520            if not callable(transform):
2521                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2522                return ""
2523
2524            return transform(self, expression)
2525
2526        if isinstance(expression, int):
2527            return str(expression)
2528
2529        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2530            escaped = expression.replace("'", "\\'")
2531            escaped = f"\\'{expression}\\'"
2532        else:
2533            escaped = expression.replace('"', '\\"')
2534            escaped = f'"{escaped}"'
2535
2536        return escaped
2537
2538    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2539        return f"{self.sql(expression, 'this')} FORMAT JSON"
2540
2541    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2542        null_handling = expression.args.get("null_handling")
2543        null_handling = f" {null_handling}" if null_handling else ""
2544
2545        unique_keys = expression.args.get("unique_keys")
2546        if unique_keys is not None:
2547            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2548        else:
2549            unique_keys = ""
2550
2551        return_type = self.sql(expression, "return_type")
2552        return_type = f" RETURNING {return_type}" if return_type else ""
2553        encoding = self.sql(expression, "encoding")
2554        encoding = f" ENCODING {encoding}" if encoding else ""
2555
2556        return self.func(
2557            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2558            *expression.expressions,
2559            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2560        )
2561
2562    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2563        return self.jsonobject_sql(expression)
2564
2565    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2566        null_handling = expression.args.get("null_handling")
2567        null_handling = f" {null_handling}" if null_handling else ""
2568        return_type = self.sql(expression, "return_type")
2569        return_type = f" RETURNING {return_type}" if return_type else ""
2570        strict = " STRICT" if expression.args.get("strict") else ""
2571        return self.func(
2572            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2573        )
2574
2575    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2576        this = self.sql(expression, "this")
2577        order = self.sql(expression, "order")
2578        null_handling = expression.args.get("null_handling")
2579        null_handling = f" {null_handling}" if null_handling else ""
2580        return_type = self.sql(expression, "return_type")
2581        return_type = f" RETURNING {return_type}" if return_type else ""
2582        strict = " STRICT" if expression.args.get("strict") else ""
2583        return self.func(
2584            "JSON_ARRAYAGG",
2585            this,
2586            suffix=f"{order}{null_handling}{return_type}{strict})",
2587        )
2588
2589    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2590        path = self.sql(expression, "path")
2591        path = f" PATH {path}" if path else ""
2592        nested_schema = self.sql(expression, "nested_schema")
2593
2594        if nested_schema:
2595            return f"NESTED{path} {nested_schema}"
2596
2597        this = self.sql(expression, "this")
2598        kind = self.sql(expression, "kind")
2599        kind = f" {kind}" if kind else ""
2600        return f"{this}{kind}{path}"
2601
2602    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2603        return self.func("COLUMNS", *expression.expressions)
2604
2605    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2606        this = self.sql(expression, "this")
2607        path = self.sql(expression, "path")
2608        path = f", {path}" if path else ""
2609        error_handling = expression.args.get("error_handling")
2610        error_handling = f" {error_handling}" if error_handling else ""
2611        empty_handling = expression.args.get("empty_handling")
2612        empty_handling = f" {empty_handling}" if empty_handling else ""
2613        schema = self.sql(expression, "schema")
2614        return self.func(
2615            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2616        )
2617
2618    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2619        this = self.sql(expression, "this")
2620        kind = self.sql(expression, "kind")
2621        path = self.sql(expression, "path")
2622        path = f" {path}" if path else ""
2623        as_json = " AS JSON" if expression.args.get("as_json") else ""
2624        return f"{this} {kind}{path}{as_json}"
2625
2626    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2627        this = self.sql(expression, "this")
2628        path = self.sql(expression, "path")
2629        path = f", {path}" if path else ""
2630        expressions = self.expressions(expression)
2631        with_ = (
2632            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2633            if expressions
2634            else ""
2635        )
2636        return f"OPENJSON({this}{path}){with_}"
2637
2638    def in_sql(self, expression: exp.In) -> str:
2639        query = expression.args.get("query")
2640        unnest = expression.args.get("unnest")
2641        field = expression.args.get("field")
2642        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2643
2644        if query:
2645            in_sql = self.wrap(self.sql(query))
2646        elif unnest:
2647            in_sql = self.in_unnest_op(unnest)
2648        elif field:
2649            in_sql = self.sql(field)
2650        else:
2651            in_sql = f"({self.expressions(expression, flat=True)})"
2652
2653        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2654
2655    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2656        return f"(SELECT {self.sql(unnest)})"
2657
2658    def interval_sql(self, expression: exp.Interval) -> str:
2659        unit = self.sql(expression, "unit")
2660        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2661            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2662        unit = f" {unit}" if unit else ""
2663
2664        if self.SINGLE_STRING_INTERVAL:
2665            this = expression.this.name if expression.this else ""
2666            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2667
2668        this = self.sql(expression, "this")
2669        if this:
2670            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2671            this = f" {this}" if unwrapped else f" ({this})"
2672
2673        return f"INTERVAL{this}{unit}"
2674
2675    def return_sql(self, expression: exp.Return) -> str:
2676        return f"RETURN {self.sql(expression, 'this')}"
2677
2678    def reference_sql(self, expression: exp.Reference) -> str:
2679        this = self.sql(expression, "this")
2680        expressions = self.expressions(expression, flat=True)
2681        expressions = f"({expressions})" if expressions else ""
2682        options = self.expressions(expression, key="options", flat=True, sep=" ")
2683        options = f" {options}" if options else ""
2684        return f"REFERENCES {this}{expressions}{options}"
2685
2686    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2687        return self.func(self.sql(expression, "this"), *expression.expressions)
2688
2689    def paren_sql(self, expression: exp.Paren) -> str:
2690        if isinstance(expression.unnest(), exp.Select):
2691            sql = self.wrap(expression)
2692        else:
2693            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2694            sql = f"({sql}{self.seg(')', sep='')}"
2695
2696        return self.prepend_ctes(expression, sql)
2697
2698    def neg_sql(self, expression: exp.Neg) -> str:
2699        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2700        this_sql = self.sql(expression, "this")
2701        sep = " " if this_sql[0] == "-" else ""
2702        return f"-{sep}{this_sql}"
2703
2704    def not_sql(self, expression: exp.Not) -> str:
2705        return f"NOT {self.sql(expression, 'this')}"
2706
2707    def alias_sql(self, expression: exp.Alias) -> str:
2708        alias = self.sql(expression, "alias")
2709        alias = f" AS {alias}" if alias else ""
2710        return f"{self.sql(expression, 'this')}{alias}"
2711
2712    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2713        alias = expression.args["alias"]
2714        identifier_alias = isinstance(alias, exp.Identifier)
2715
2716        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2717            alias.replace(exp.Literal.string(alias.output_name))
2718        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2719            alias.replace(exp.to_identifier(alias.output_name))
2720
2721        return self.alias_sql(expression)
2722
2723    def aliases_sql(self, expression: exp.Aliases) -> str:
2724        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
2725
2726    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2727        this = self.sql(expression, "this")
2728        index = self.sql(expression, "expression")
2729        return f"{this} AT {index}"
2730
2731    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2732        this = self.sql(expression, "this")
2733        zone = self.sql(expression, "zone")
2734        return f"{this} AT TIME ZONE {zone}"
2735
2736    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2737        this = self.sql(expression, "this")
2738        zone = self.sql(expression, "zone")
2739        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
2740
2741    def add_sql(self, expression: exp.Add) -> str:
2742        return self.binary(expression, "+")
2743
2744    def and_sql(self, expression: exp.And) -> str:
2745        return self.connector_sql(expression, "AND")
2746
2747    def xor_sql(self, expression: exp.Xor) -> str:
2748        return self.connector_sql(expression, "XOR")
2749
2750    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2751        if not self.pretty:
2752            return self.binary(expression, op)
2753
2754        sqls = tuple(
2755            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2756            for i, e in enumerate(expression.flatten(unnest=False))
2757        )
2758
2759        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2760        return f"{sep}{op} ".join(sqls)
2761
2762    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2763        return self.binary(expression, "&")
2764
2765    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2766        return self.binary(expression, "<<")
2767
2768    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2769        return f"~{self.sql(expression, 'this')}"
2770
2771    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2772        return self.binary(expression, "|")
2773
2774    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2775        return self.binary(expression, ">>")
2776
2777    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2778        return self.binary(expression, "^")
2779
2780    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2781        format_sql = self.sql(expression, "format")
2782        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2783        to_sql = self.sql(expression, "to")
2784        to_sql = f" {to_sql}" if to_sql else ""
2785        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
2786
2787    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2788        zone = self.sql(expression, "this")
2789        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
2790
2791    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2792        return self.func("CURRENT_TIMESTAMP", expression.this)
2793
2794    def collate_sql(self, expression: exp.Collate) -> str:
2795        if self.COLLATE_IS_FUNC:
2796            return self.function_fallback_sql(expression)
2797        return self.binary(expression, "COLLATE")
2798
2799    def command_sql(self, expression: exp.Command) -> str:
2800        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
2801
2802    def comment_sql(self, expression: exp.Comment) -> str:
2803        this = self.sql(expression, "this")
2804        kind = expression.args["kind"]
2805        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2806        expression_sql = self.sql(expression, "expression")
2807        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2808
2809    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2810        this = self.sql(expression, "this")
2811        delete = " DELETE" if expression.args.get("delete") else ""
2812        recompress = self.sql(expression, "recompress")
2813        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2814        to_disk = self.sql(expression, "to_disk")
2815        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2816        to_volume = self.sql(expression, "to_volume")
2817        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2818        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2819
2820    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2821        where = self.sql(expression, "where")
2822        group = self.sql(expression, "group")
2823        aggregates = self.expressions(expression, key="aggregates")
2824        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2825
2826        if not (where or group or aggregates) and len(expression.expressions) == 1:
2827            return f"TTL {self.expressions(expression, flat=True)}"
2828
2829        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2830
2831    def transaction_sql(self, expression: exp.Transaction) -> str:
2832        return "BEGIN"
2833
2834    def commit_sql(self, expression: exp.Commit) -> str:
2835        chain = expression.args.get("chain")
2836        if chain is not None:
2837            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2838
2839        return f"COMMIT{chain or ''}"
2840
2841    def rollback_sql(self, expression: exp.Rollback) -> str:
2842        savepoint = expression.args.get("savepoint")
2843        savepoint = f" TO {savepoint}" if savepoint else ""
2844        return f"ROLLBACK{savepoint}"
2845
2846    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2847        this = self.sql(expression, "this")
2848
2849        dtype = self.sql(expression, "dtype")
2850        if dtype:
2851            collate = self.sql(expression, "collate")
2852            collate = f" COLLATE {collate}" if collate else ""
2853            using = self.sql(expression, "using")
2854            using = f" USING {using}" if using else ""
2855            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2856
2857        default = self.sql(expression, "default")
2858        if default:
2859            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2860
2861        comment = self.sql(expression, "comment")
2862        if comment:
2863            return f"ALTER COLUMN {this} COMMENT {comment}"
2864
2865        if not expression.args.get("drop"):
2866            self.unsupported("Unsupported ALTER COLUMN syntax")
2867
2868        return f"ALTER COLUMN {this} DROP DEFAULT"
2869
2870    def renametable_sql(self, expression: exp.RenameTable) -> str:
2871        if not self.RENAME_TABLE_WITH_DB:
2872            # Remove db from tables
2873            expression = expression.transform(
2874                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2875            )
2876        this = self.sql(expression, "this")
2877        return f"RENAME TO {this}"
2878
2879    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2880        exists = " IF EXISTS" if expression.args.get("exists") else ""
2881        old_column = self.sql(expression, "this")
2882        new_column = self.sql(expression, "to")
2883        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
2884
2885    def altertable_sql(self, expression: exp.AlterTable) -> str:
2886        actions = expression.args["actions"]
2887
2888        if isinstance(actions[0], exp.ColumnDef):
2889            actions = self.add_column_sql(expression)
2890        elif isinstance(actions[0], exp.Schema):
2891            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2892        elif isinstance(actions[0], exp.Delete):
2893            actions = self.expressions(expression, key="actions", flat=True)
2894        else:
2895            actions = self.expressions(expression, key="actions", flat=True)
2896
2897        exists = " IF EXISTS" if expression.args.get("exists") else ""
2898        only = " ONLY" if expression.args.get("only") else ""
2899        options = self.expressions(expression, key="options")
2900        options = f", {options}" if options else ""
2901        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
2902
2903    def add_column_sql(self, expression: exp.AlterTable) -> str:
2904        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2905            return self.expressions(
2906                expression,
2907                key="actions",
2908                prefix="ADD COLUMN ",
2909            )
2910        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
2911
2912    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2913        expressions = self.expressions(expression)
2914        exists = " IF EXISTS " if expression.args.get("exists") else " "
2915        return f"DROP{exists}{expressions}"
2916
2917    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2918        return f"ADD {self.expressions(expression)}"
2919
2920    def distinct_sql(self, expression: exp.Distinct) -> str:
2921        this = self.expressions(expression, flat=True)
2922
2923        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2924            case = exp.case()
2925            for arg in expression.expressions:
2926                case = case.when(arg.is_(exp.null()), exp.null())
2927            this = self.sql(case.else_(f"({this})"))
2928
2929        this = f" {this}" if this else ""
2930
2931        on = self.sql(expression, "on")
2932        on = f" ON {on}" if on else ""
2933        return f"DISTINCT{this}{on}"
2934
2935    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2936        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
2937
2938    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2939        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
2940
2941    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2942        this_sql = self.sql(expression, "this")
2943        expression_sql = self.sql(expression, "expression")
2944        kind = "MAX" if expression.args.get("max") else "MIN"
2945        return f"{this_sql} HAVING {kind} {expression_sql}"
2946
2947    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
2948        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
2949            # The first modifier here will be the one closest to the AggFunc's arg
2950            mods = sorted(
2951                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
2952                key=lambda x: 0
2953                if isinstance(x, exp.HavingMax)
2954                else (1 if isinstance(x, exp.Order) else 2),
2955            )
2956
2957            if mods:
2958                mod = mods[0]
2959                this = expression.__class__(this=mod.this.copy())
2960                this.meta["inline"] = True
2961                mod.this.replace(this)
2962                return self.sql(expression.this)
2963
2964            agg_func = expression.find(exp.AggFunc)
2965
2966            if agg_func:
2967                return self.sql(agg_func)[:-1] + f" {text})"
2968
2969        return f"{self.sql(expression, 'this')} {text}"
2970
2971    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2972        return self.sql(
2973            exp.Cast(
2974                this=exp.Div(this=expression.this, expression=expression.expression),
2975                to=exp.DataType(this=exp.DataType.Type.INT),
2976            )
2977        )
2978
2979    def dpipe_sql(self, expression: exp.DPipe) -> str:
2980        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2981            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2982        return self.binary(expression, "||")
2983
2984    def div_sql(self, expression: exp.Div) -> str:
2985        l, r = expression.left, expression.right
2986
2987        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2988            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2989
2990        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2991            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
2992                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2993
2994        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2995            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2996                return self.sql(
2997                    exp.cast(
2998                        l / r,
2999                        to=exp.DataType.Type.BIGINT,
3000                    )
3001                )
3002
3003        return self.binary(expression, "/")
3004
3005    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3006        return self.binary(expression, "OVERLAPS")
3007
3008    def distance_sql(self, expression: exp.Distance) -> str:
3009        return self.binary(expression, "<->")
3010
3011    def dot_sql(self, expression: exp.Dot) -> str:
3012        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
3013
3014    def eq_sql(self, expression: exp.EQ) -> str:
3015        return self.binary(expression, "=")
3016
3017    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3018        return self.binary(expression, ":=")
3019
3020    def escape_sql(self, expression: exp.Escape) -> str:
3021        return self.binary(expression, "ESCAPE")
3022
3023    def glob_sql(self, expression: exp.Glob) -> str:
3024        return self.binary(expression, "GLOB")
3025
3026    def gt_sql(self, expression: exp.GT) -> str:
3027        return self.binary(expression, ">")
3028
3029    def gte_sql(self, expression: exp.GTE) -> str:
3030        return self.binary(expression, ">=")
3031
3032    def ilike_sql(self, expression: exp.ILike) -> str:
3033        return self.binary(expression, "ILIKE")
3034
3035    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
3036        return self.binary(expression, "ILIKE ANY")
3037
3038    def is_sql(self, expression: exp.Is) -> str:
3039        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3040            return self.sql(
3041                expression.this if expression.expression.this else exp.not_(expression.this)
3042            )
3043        return self.binary(expression, "IS")
3044
3045    def like_sql(self, expression: exp.Like) -> str:
3046        return self.binary(expression, "LIKE")
3047
3048    def likeany_sql(self, expression: exp.LikeAny) -> str:
3049        return self.binary(expression, "LIKE ANY")
3050
3051    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3052        return self.binary(expression, "SIMILAR TO")
3053
3054    def lt_sql(self, expression: exp.LT) -> str:
3055        return self.binary(expression, "<")
3056
3057    def lte_sql(self, expression: exp.LTE) -> str:
3058        return self.binary(expression, "<=")
3059
3060    def mod_sql(self, expression: exp.Mod) -> str:
3061        return self.binary(expression, "%")
3062
3063    def mul_sql(self, expression: exp.Mul) -> str:
3064        return self.binary(expression, "*")
3065
3066    def neq_sql(self, expression: exp.NEQ) -> str:
3067        return self.binary(expression, "<>")
3068
3069    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3070        return self.binary(expression, "IS NOT DISTINCT FROM")
3071
3072    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3073        return self.binary(expression, "IS DISTINCT FROM")
3074
3075    def or_sql(self, expression: exp.Or) -> str:
3076        return self.connector_sql(expression, "OR")
3077
3078    def slice_sql(self, expression: exp.Slice) -> str:
3079        return self.binary(expression, ":")
3080
3081    def sub_sql(self, expression: exp.Sub) -> str:
3082        return self.binary(expression, "-")
3083
3084    def trycast_sql(self, expression: exp.TryCast) -> str:
3085        return self.cast_sql(expression, safe_prefix="TRY_")
3086
3087    def log_sql(self, expression: exp.Log) -> str:
3088        this = expression.this
3089        expr = expression.expression
3090
3091        if not self.dialect.LOG_BASE_FIRST:
3092            this, expr = expr, this
3093
3094        return self.func("LOG", this, expr)
3095
3096    def use_sql(self, expression: exp.Use) -> str:
3097        kind = self.sql(expression, "kind")
3098        kind = f" {kind}" if kind else ""
3099        this = self.sql(expression, "this")
3100        this = f" {this}" if this else ""
3101        return f"USE{kind}{this}"
3102
3103    def binary(self, expression: exp.Binary, op: str) -> str:
3104        op = self.maybe_comment(op, comments=expression.comments)
3105        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
3106
3107    def function_fallback_sql(self, expression: exp.Func) -> str:
3108        args = []
3109
3110        for key in expression.arg_types:
3111            arg_value = expression.args.get(key)
3112
3113            if isinstance(arg_value, list):
3114                for value in arg_value:
3115                    args.append(value)
3116            elif arg_value is not None:
3117                args.append(arg_value)
3118
3119        if self.normalize_functions:
3120            name = expression.sql_name()
3121        else:
3122            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3123
3124        return self.func(name, *args)
3125
3126    def func(
3127        self,
3128        name: str,
3129        *args: t.Optional[exp.Expression | str],
3130        prefix: str = "(",
3131        suffix: str = ")",
3132    ) -> str:
3133        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
3134
3135    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3136        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3137        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3138            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3139        return ", ".join(arg_sqls)
3140
3141    def text_width(self, args: t.Iterable) -> int:
3142        return sum(len(arg) for arg in args)
3143
3144    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3145        return format_time(
3146            self.sql(expression, "format"),
3147            self.dialect.INVERSE_TIME_MAPPING,
3148            self.dialect.INVERSE_TIME_TRIE,
3149        )
3150
3151    def expressions(
3152        self,
3153        expression: t.Optional[exp.Expression] = None,
3154        key: t.Optional[str] = None,
3155        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3156        flat: bool = False,
3157        indent: bool = True,
3158        skip_first: bool = False,
3159        sep: str = ", ",
3160        prefix: str = "",
3161    ) -> str:
3162        expressions = expression.args.get(key or "expressions") if expression else sqls
3163
3164        if not expressions:
3165            return ""
3166
3167        if flat:
3168            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3169
3170        num_sqls = len(expressions)
3171
3172        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3173        pad = " " * self.pad
3174        stripped_sep = sep.strip()
3175
3176        result_sqls = []
3177        for i, e in enumerate(expressions):
3178            sql = self.sql(e, comment=False)
3179            if not sql:
3180                continue
3181
3182            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3183
3184            if self.pretty:
3185                if self.leading_comma:
3186                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3187                else:
3188                    result_sqls.append(
3189                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3190                    )
3191            else:
3192                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3193
3194        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3195        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
3196
3197    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3198        flat = flat or isinstance(expression.parent, exp.Properties)
3199        expressions_sql = self.expressions(expression, flat=flat)
3200        if flat:
3201            return f"{op} {expressions_sql}"
3202        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3203
3204    def naked_property(self, expression: exp.Property) -> str:
3205        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3206        if not property_name:
3207            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3208        return f"{property_name} {self.sql(expression, 'this')}"
3209
3210    def tag_sql(self, expression: exp.Tag) -> str:
3211        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3212
3213    def token_sql(self, token_type: TokenType) -> str:
3214        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3215
3216    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3217        this = self.sql(expression, "this")
3218        expressions = self.no_identify(self.expressions, expression)
3219        expressions = (
3220            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3221        )
3222        return f"{this}{expressions}"
3223
3224    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3225        this = self.sql(expression, "this")
3226        expressions = self.expressions(expression, flat=True)
3227        return f"{this}({expressions})"
3228
3229    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3230        return self.binary(expression, "=>")
3231
3232    def when_sql(self, expression: exp.When) -> str:
3233        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3234        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3235        condition = self.sql(expression, "condition")
3236        condition = f" AND {condition}" if condition else ""
3237
3238        then_expression = expression.args.get("then")
3239        if isinstance(then_expression, exp.Insert):
3240            then = f"INSERT {self.sql(then_expression, 'this')}"
3241            if "expression" in then_expression.args:
3242                then += f" VALUES {self.sql(then_expression, 'expression')}"
3243        elif isinstance(then_expression, exp.Update):
3244            if isinstance(then_expression.args.get("expressions"), exp.Star):
3245                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3246            else:
3247                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3248        else:
3249            then = self.sql(then_expression)
3250        return f"WHEN {matched}{source}{condition} THEN {then}"
3251
3252    def merge_sql(self, expression: exp.Merge) -> str:
3253        table = expression.this
3254        table_alias = ""
3255
3256        hints = table.args.get("hints")
3257        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3258            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3259            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3260
3261        this = self.sql(table)
3262        using = f"USING {self.sql(expression, 'using')}"
3263        on = f"ON {self.sql(expression, 'on')}"
3264        expressions = self.expressions(expression, sep=" ")
3265
3266        return self.prepend_ctes(
3267            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3268        )
3269
3270    def tochar_sql(self, expression: exp.ToChar) -> str:
3271        if expression.args.get("format"):
3272            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3273
3274        return self.sql(exp.cast(expression.this, "text"))
3275
3276    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3277        this = self.sql(expression, "this")
3278        kind = self.sql(expression, "kind")
3279        settings_sql = self.expressions(expression, key="settings", sep=" ")
3280        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3281        return f"{this}({kind}{args})"
3282
3283    def dictrange_sql(self, expression: exp.DictRange) -> str:
3284        this = self.sql(expression, "this")
3285        max = self.sql(expression, "max")
3286        min = self.sql(expression, "min")
3287        return f"{this}(MIN {min} MAX {max})"
3288
3289    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3290        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
3291
3292    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3293        return ""
3294
3295    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3296        expressions = self.expressions(expression, key="expressions", flat=True)
3297        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3298        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3299        buckets = self.sql(expression, "buckets")
3300        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3301
3302    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3303        this = self.sql(expression, "this")
3304        having = self.sql(expression, "having")
3305
3306        if having:
3307            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3308
3309        return self.func("ANY_VALUE", this)
3310
3311    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3312        transform = self.func("TRANSFORM", *expression.expressions)
3313        row_format_before = self.sql(expression, "row_format_before")
3314        row_format_before = f" {row_format_before}" if row_format_before else ""
3315        record_writer = self.sql(expression, "record_writer")
3316        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3317        using = f" USING {self.sql(expression, 'command_script')}"
3318        schema = self.sql(expression, "schema")
3319        schema = f" AS {schema}" if schema else ""
3320        row_format_after = self.sql(expression, "row_format_after")
3321        row_format_after = f" {row_format_after}" if row_format_after else ""
3322        record_reader = self.sql(expression, "record_reader")
3323        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3324        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3325
3326    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3327        key_block_size = self.sql(expression, "key_block_size")
3328        if key_block_size:
3329            return f"KEY_BLOCK_SIZE = {key_block_size}"
3330
3331        using = self.sql(expression, "using")
3332        if using:
3333            return f"USING {using}"
3334
3335        parser = self.sql(expression, "parser")
3336        if parser:
3337            return f"WITH PARSER {parser}"
3338
3339        comment = self.sql(expression, "comment")
3340        if comment:
3341            return f"COMMENT {comment}"
3342
3343        visible = expression.args.get("visible")
3344        if visible is not None:
3345            return "VISIBLE" if visible else "INVISIBLE"
3346
3347        engine_attr = self.sql(expression, "engine_attr")
3348        if engine_attr:
3349            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3350
3351        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3352        if secondary_engine_attr:
3353            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3354
3355        self.unsupported("Unsupported index constraint option.")
3356        return ""
3357
3358    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3359        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3360        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
3361
3362    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3363        kind = self.sql(expression, "kind")
3364        kind = f"{kind} INDEX" if kind else "INDEX"
3365        this = self.sql(expression, "this")
3366        this = f" {this}" if this else ""
3367        index_type = self.sql(expression, "index_type")
3368        index_type = f" USING {index_type}" if index_type else ""
3369        schema = self.sql(expression, "schema")
3370        schema = f" {schema}" if schema else ""
3371        options = self.expressions(expression, key="options", sep=" ")
3372        options = f" {options}" if options else ""
3373        return f"{kind}{this}{index_type}{schema}{options}"
3374
3375    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3376        if self.NVL2_SUPPORTED:
3377            return self.function_fallback_sql(expression)
3378
3379        case = exp.Case().when(
3380            expression.this.is_(exp.null()).not_(copy=False),
3381            expression.args["true"],
3382            copy=False,
3383        )
3384        else_cond = expression.args.get("false")
3385        if else_cond:
3386            case.else_(else_cond, copy=False)
3387
3388        return self.sql(case)
3389
3390    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3391        this = self.sql(expression, "this")
3392        expr = self.sql(expression, "expression")
3393        iterator = self.sql(expression, "iterator")
3394        condition = self.sql(expression, "condition")
3395        condition = f" IF {condition}" if condition else ""
3396        return f"{this} FOR {expr} IN {iterator}{condition}"
3397
3398    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3399        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
3400
3401    def opclass_sql(self, expression: exp.Opclass) -> str:
3402        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
3403
3404    def predict_sql(self, expression: exp.Predict) -> str:
3405        model = self.sql(expression, "this")
3406        model = f"MODEL {model}"
3407        table = self.sql(expression, "expression")
3408        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3409        parameters = self.sql(expression, "params_struct")
3410        return self.func("PREDICT", model, table, parameters or None)
3411
3412    def forin_sql(self, expression: exp.ForIn) -> str:
3413        this = self.sql(expression, "this")
3414        expression_sql = self.sql(expression, "expression")
3415        return f"FOR {this} DO {expression_sql}"
3416
3417    def refresh_sql(self, expression: exp.Refresh) -> str:
3418        this = self.sql(expression, "this")
3419        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3420        return f"REFRESH {table}{this}"
3421
3422    def operator_sql(self, expression: exp.Operator) -> str:
3423        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
3424
3425    def toarray_sql(self, expression: exp.ToArray) -> str:
3426        arg = expression.this
3427        if not arg.type:
3428            from sqlglot.optimizer.annotate_types import annotate_types
3429
3430            arg = annotate_types(arg)
3431
3432        if arg.is_type(exp.DataType.Type.ARRAY):
3433            return self.sql(arg)
3434
3435        cond_for_null = arg.is_(exp.null())
3436        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3437
3438    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3439        this = expression.this
3440        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3441            return self.sql(this)
3442
3443        return self.sql(exp.cast(this, "time"))
3444
3445    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3446        this = expression.this
3447        time_format = self.format_time(expression)
3448
3449        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3450            return self.sql(
3451                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3452            )
3453
3454        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3455            return self.sql(this)
3456
3457        return self.sql(exp.cast(this, "date"))
3458
3459    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3460        return self.sql(
3461            exp.func(
3462                "DATEDIFF",
3463                expression.this,
3464                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3465                "day",
3466            )
3467        )
3468
3469    def lastday_sql(self, expression: exp.LastDay) -> str:
3470        if self.LAST_DAY_SUPPORTS_DATE_PART:
3471            return self.function_fallback_sql(expression)
3472
3473        unit = expression.text("unit")
3474        if unit and unit != "MONTH":
3475            self.unsupported("Date parts are not supported in LAST_DAY.")
3476
3477        return self.func("LAST_DAY", expression.this)
3478
3479    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3480        if self.CAN_IMPLEMENT_ARRAY_ANY:
3481            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3482            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3483            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3484            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3485
3486        from sqlglot.dialects import Dialect
3487
3488        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3489        if self.dialect.__class__ != Dialect:
3490            self.unsupported("ARRAY_ANY is unsupported")
3491
3492        return self.function_fallback_sql(expression)
3493
3494    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
3495        this = expression.this
3496        if isinstance(this, exp.JSONPathWildcard):
3497            this = self.json_path_part(this)
3498            return f".{this}" if this else ""
3499
3500        if exp.SAFE_IDENTIFIER_RE.match(this):
3501            return f".{this}"
3502
3503        this = self.json_path_part(this)
3504        return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}"
3505
3506    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
3507        this = self.json_path_part(expression.this)
3508        return f"[{this}]" if this else ""
3509
3510    def _simplify_unless_literal(self, expression: E) -> E:
3511        if not isinstance(expression, exp.Literal):
3512            from sqlglot.optimizer.simplify import simplify
3513
3514            expression = simplify(expression, dialect=self.dialect)
3515
3516        return expression
3517
3518    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
3519        expression.set("is_end_exclusive", None)
3520        return self.function_fallback_sql(expression)
3521
3522    def struct_sql(self, expression: exp.Struct) -> str:
3523        expression.set(
3524            "expressions",
3525            [
3526                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
3527                for e in expression.expressions
3528            ],
3529        )
3530
3531        return self.function_fallback_sql(expression)
3532
3533    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
3534        low = self.sql(expression, "this")
3535        high = self.sql(expression, "expression")
3536
3537        return f"{low} TO {high}"
3538
3539    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
3540        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
3541        tables = f" {self.expressions(expression)}"
3542
3543        exists = " IF EXISTS" if expression.args.get("exists") else ""
3544
3545        on_cluster = self.sql(expression, "cluster")
3546        on_cluster = f" {on_cluster}" if on_cluster else ""
3547
3548        identity = self.sql(expression, "identity")
3549        identity = f" {identity} IDENTITY" if identity else ""
3550
3551        option = self.sql(expression, "option")
3552        option = f" {option}" if option else ""
3553
3554        partition = self.sql(expression, "partition")
3555        partition = f" {partition}" if partition else ""
3556
3557        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
3558
3559    # This transpiles T-SQL's CONVERT function
3560    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
3561    def convert_sql(self, expression: exp.Convert) -> str:
3562        to = expression.this
3563        value = expression.expression
3564        style = expression.args.get("style")
3565        safe = expression.args.get("safe")
3566        strict = expression.args.get("strict")
3567
3568        if not to or not value:
3569            return ""
3570
3571        # Retrieve length of datatype and override to default if not specified
3572        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
3573            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
3574
3575        transformed: t.Optional[exp.Expression] = None
3576        cast = exp.Cast if strict else exp.TryCast
3577
3578        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
3579        if isinstance(style, exp.Literal) and style.is_int:
3580            from sqlglot.dialects.tsql import TSQL
3581
3582            style_value = style.name
3583            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
3584            if not converted_style:
3585                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
3586
3587            fmt = exp.Literal.string(converted_style)
3588
3589            if to.this == exp.DataType.Type.DATE:
3590                transformed = exp.StrToDate(this=value, format=fmt)
3591            elif to.this == exp.DataType.Type.DATETIME:
3592                transformed = exp.StrToTime(this=value, format=fmt)
3593            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
3594                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
3595            elif to.this == exp.DataType.Type.TEXT:
3596                transformed = exp.TimeToStr(this=value, format=fmt)
3597
3598        if not transformed:
3599            transformed = cast(this=value, to=to, safe=safe)
3600
3601        return self.sql(transformed)

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. Default: 2.
  • indent: The indentation size in a formatted string. 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.Dialect, Type[sqlglot.dialects.dialect.Dialect], NoneType] = None)
514    def __init__(
515        self,
516        pretty: t.Optional[bool] = None,
517        identify: str | bool = False,
518        normalize: bool = False,
519        pad: int = 2,
520        indent: int = 2,
521        normalize_functions: t.Optional[str | bool] = None,
522        unsupported_level: ErrorLevel = ErrorLevel.WARN,
523        max_unsupported: int = 3,
524        leading_comma: bool = False,
525        max_text_width: int = 80,
526        comments: bool = True,
527        dialect: DialectType = None,
528    ):
529        import sqlglot
530        from sqlglot.dialects import Dialect
531
532        self.pretty = pretty if pretty is not None else sqlglot.pretty
533        self.identify = identify
534        self.normalize = normalize
535        self.pad = pad
536        self._indent = indent
537        self.unsupported_level = unsupported_level
538        self.max_unsupported = max_unsupported
539        self.leading_comma = leading_comma
540        self.max_text_width = max_text_width
541        self.comments = comments
542        self.dialect = Dialect.get_or_raise(dialect)
543
544        # This is both a Dialect property and a Generator argument, so we prioritize the latter
545        self.normalize_functions = (
546            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
547        )
548
549        self.unsupported_messages: t.List[str] = []
550        self._escaped_quote_end: str = (
551            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
552        )
553        self._escaped_identifier_end: str = (
554            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
555        )
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.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <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.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateAdd'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <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.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <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.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <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.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.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <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.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXPLICIT_UNION = False
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
COLUMN_JOIN_MARKS_SUPPORTED = False
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
TYPE_MAPPING = {<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET'}
STAR_MAPPING = {'except': 'EXCEPT', 'replace': 'REPLACE'}
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>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
PROPERTIES_LOCATION = {<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.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.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <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.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <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.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.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.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.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.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
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.CHAR: 'CHAR'>, <Type.VARCHAR: 'VARCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>, <Type.NCHAR: 'NCHAR'>}
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:
557    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
558        """
559        Generates the SQL string corresponding to the given syntax tree.
560
561        Args:
562            expression: The syntax tree.
563            copy: Whether to copy the expression. The generator performs mutations so
564                it is safer to copy.
565
566        Returns:
567            The SQL string corresponding to `expression`.
568        """
569        if copy:
570            expression = expression.copy()
571
572        expression = self.preprocess(expression)
573
574        self.unsupported_messages = []
575        sql = self.sql(expression).strip()
576
577        if self.pretty:
578            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
579
580        if self.unsupported_level == ErrorLevel.IGNORE:
581            return sql
582
583        if self.unsupported_level == ErrorLevel.WARN:
584            for msg in self.unsupported_messages:
585                logger.warning(msg)
586        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
587            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
588
589        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:
591    def preprocess(self, expression: exp.Expression) -> exp.Expression:
592        """Apply generic preprocessing transformations to a given expression."""
593        if (
594            not expression.parent
595            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
596            and any(node.parent is not expression for node in expression.find_all(exp.With))
597        ):
598            from sqlglot.transforms import move_ctes_to_top_level
599
600            expression = move_ctes_to_top_level(expression)
601
602        if self.ENSURE_BOOLS:
603            from sqlglot.transforms import ensure_bools
604
605            expression = ensure_bools(expression)
606
607        return expression

Apply generic preprocessing transformations to a given expression.

def unsupported(self, message: str) -> None:
609    def unsupported(self, message: str) -> None:
610        if self.unsupported_level == ErrorLevel.IMMEDIATE:
611            raise UnsupportedError(message)
612        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
614    def sep(self, sep: str = " ") -> str:
615        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
617    def seg(self, sql: str, sep: str = " ") -> str:
618        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
620    def pad_comment(self, comment: str) -> str:
621        comment = " " + comment if comment[0].strip() else comment
622        comment = comment + " " if comment[-1].strip() else comment
623        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None) -> str:
625    def maybe_comment(
626        self,
627        sql: str,
628        expression: t.Optional[exp.Expression] = None,
629        comments: t.Optional[t.List[str]] = None,
630    ) -> str:
631        comments = (
632            ((expression and expression.comments) if comments is None else comments)  # type: ignore
633            if self.comments
634            else None
635        )
636
637        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
638            return sql
639
640        comments_sql = " ".join(
641            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
642        )
643
644        if not comments_sql:
645            return sql
646
647        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
648            return (
649                f"{self.sep()}{comments_sql}{sql}"
650                if sql[0].isspace()
651                else f"{comments_sql}{self.sep()}{sql}"
652            )
653
654        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
656    def wrap(self, expression: exp.Expression | str) -> str:
657        this_sql = self.indent(
658            (
659                self.sql(expression)
660                if isinstance(expression, exp.UNWRAPPED_QUERIES)
661                else self.sql(expression, "this")
662            ),
663            level=1,
664            pad=0,
665        )
666        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
668    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
669        original = self.identify
670        self.identify = False
671        result = func(*args, **kwargs)
672        self.identify = original
673        return result
def normalize_func(self, name: str) -> str:
675    def normalize_func(self, name: str) -> str:
676        if self.normalize_functions == "upper" or self.normalize_functions is True:
677            return name.upper()
678        if self.normalize_functions == "lower":
679            return name.lower()
680        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
682    def indent(
683        self,
684        sql: str,
685        level: int = 0,
686        pad: t.Optional[int] = None,
687        skip_first: bool = False,
688        skip_last: bool = False,
689    ) -> str:
690        if not self.pretty:
691            return sql
692
693        pad = self.pad if pad is None else pad
694        lines = sql.split("\n")
695
696        return "\n".join(
697            (
698                line
699                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
700                else f"{' ' * (level * self._indent + pad)}{line}"
701            )
702            for i, line in enumerate(lines)
703        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
705    def sql(
706        self,
707        expression: t.Optional[str | exp.Expression],
708        key: t.Optional[str] = None,
709        comment: bool = True,
710    ) -> str:
711        if not expression:
712            return ""
713
714        if isinstance(expression, str):
715            return expression
716
717        if key:
718            value = expression.args.get(key)
719            if value:
720                return self.sql(value)
721            return ""
722
723        transform = self.TRANSFORMS.get(expression.__class__)
724
725        if callable(transform):
726            sql = transform(self, expression)
727        elif isinstance(expression, exp.Expression):
728            exp_handler_name = f"{expression.key}_sql"
729
730            if hasattr(self, exp_handler_name):
731                sql = getattr(self, exp_handler_name)(expression)
732            elif isinstance(expression, exp.Func):
733                sql = self.function_fallback_sql(expression)
734            elif isinstance(expression, exp.Property):
735                sql = self.property_sql(expression)
736            else:
737                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
738        else:
739            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
740
741        return self.maybe_comment(sql, expression) if self.comments and comment else sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
743    def uncache_sql(self, expression: exp.Uncache) -> str:
744        table = self.sql(expression, "this")
745        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
746        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
748    def cache_sql(self, expression: exp.Cache) -> str:
749        lazy = " LAZY" if expression.args.get("lazy") else ""
750        table = self.sql(expression, "this")
751        options = expression.args.get("options")
752        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
753        sql = self.sql(expression, "expression")
754        sql = f" AS{self.sep()}{sql}" if sql else ""
755        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
756        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
758    def characterset_sql(self, expression: exp.CharacterSet) -> str:
759        if isinstance(expression.parent, exp.Cast):
760            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
761        default = "DEFAULT " if expression.args.get("default") else ""
762        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
764    def column_sql(self, expression: exp.Column) -> str:
765        join_mark = " (+)" if expression.args.get("join_mark") else ""
766
767        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
768            join_mark = ""
769            self.unsupported("Outer join syntax using the (+) operator is not supported.")
770
771        column = ".".join(
772            self.sql(part)
773            for part in (
774                expression.args.get("catalog"),
775                expression.args.get("db"),
776                expression.args.get("table"),
777                expression.args.get("this"),
778            )
779            if part
780        )
781
782        return f"{column}{join_mark}"
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
784    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
785        this = self.sql(expression, "this")
786        this = f" {this}" if this else ""
787        position = self.sql(expression, "position")
788        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
790    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
791        column = self.sql(expression, "this")
792        kind = self.sql(expression, "kind")
793        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
794        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
795        kind = f"{sep}{kind}" if kind else ""
796        constraints = f" {constraints}" if constraints else ""
797        position = self.sql(expression, "position")
798        position = f" {position}" if position else ""
799
800        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
801            kind = ""
802
803        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
805    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
806        this = self.sql(expression, "this")
807        kind_sql = self.sql(expression, "kind").strip()
808        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
810    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
811        this = self.sql(expression, "this")
812        if expression.args.get("not_null"):
813            persisted = " PERSISTED NOT NULL"
814        elif expression.args.get("persisted"):
815            persisted = " PERSISTED"
816        else:
817            persisted = ""
818        return f"AS {this}{persisted}"
def autoincrementcolumnconstraint_sql(self, _) -> str:
820    def autoincrementcolumnconstraint_sql(self, _) -> str:
821        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
823    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
824        if isinstance(expression.this, list):
825            this = self.wrap(self.expressions(expression, key="this", flat=True))
826        else:
827            this = self.sql(expression, "this")
828
829        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
831    def generatedasidentitycolumnconstraint_sql(
832        self, expression: exp.GeneratedAsIdentityColumnConstraint
833    ) -> str:
834        this = ""
835        if expression.this is not None:
836            on_null = " ON NULL" if expression.args.get("on_null") else ""
837            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
838
839        start = expression.args.get("start")
840        start = f"START WITH {start}" if start else ""
841        increment = expression.args.get("increment")
842        increment = f" INCREMENT BY {increment}" if increment else ""
843        minvalue = expression.args.get("minvalue")
844        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
845        maxvalue = expression.args.get("maxvalue")
846        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
847        cycle = expression.args.get("cycle")
848        cycle_sql = ""
849
850        if cycle is not None:
851            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
852            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
853
854        sequence_opts = ""
855        if start or increment or cycle_sql:
856            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
857            sequence_opts = f" ({sequence_opts.strip()})"
858
859        expr = self.sql(expression, "expression")
860        expr = f"({expr})" if expr else "IDENTITY"
861
862        return f"GENERATED{this} AS {expr}{sequence_opts}"
def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str:
864    def generatedasrowcolumnconstraint_sql(
865        self, expression: exp.GeneratedAsRowColumnConstraint
866    ) -> str:
867        start = "START" if expression.args.get("start") else "END"
868        hidden = " HIDDEN" if expression.args.get("hidden") else ""
869        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str:
871    def periodforsystemtimeconstraint_sql(
872        self, expression: exp.PeriodForSystemTimeConstraint
873    ) -> str:
874        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
876    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
877        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str:
879    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
880        return f"AS {self.sql(expression, 'this')}"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
882    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
883        desc = expression.args.get("desc")
884        if desc is not None:
885            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
886        return "PRIMARY KEY"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
888    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
889        this = self.sql(expression, "this")
890        this = f" {this}" if this else ""
891        index_type = expression.args.get("index_type")
892        index_type = f" USING {index_type}" if index_type else ""
893        on_conflict = self.sql(expression, "on_conflict")
894        on_conflict = f" {on_conflict}" if on_conflict else ""
895        return f"UNIQUE{this}{index_type}{on_conflict}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str:
897    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
898        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
900    def create_sql(self, expression: exp.Create) -> str:
901        kind = self.sql(expression, "kind")
902        properties = expression.args.get("properties")
903        properties_locs = self.locate_properties(properties) if properties else defaultdict()
904
905        this = self.createable_sql(expression, properties_locs)
906
907        properties_sql = ""
908        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
909            exp.Properties.Location.POST_WITH
910        ):
911            properties_sql = self.sql(
912                exp.Properties(
913                    expressions=[
914                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
915                        *properties_locs[exp.Properties.Location.POST_WITH],
916                    ]
917                )
918            )
919
920        begin = " BEGIN" if expression.args.get("begin") else ""
921        end = " END" if expression.args.get("end") else ""
922
923        expression_sql = self.sql(expression, "expression")
924        if expression_sql:
925            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
926
927            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
928                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
929                    postalias_props_sql = self.properties(
930                        exp.Properties(
931                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
932                        ),
933                        wrapped=False,
934                    )
935                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
936                else:
937                    expression_sql = f" AS{expression_sql}"
938
939        postindex_props_sql = ""
940        if properties_locs.get(exp.Properties.Location.POST_INDEX):
941            postindex_props_sql = self.properties(
942                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
943                wrapped=False,
944                prefix=" ",
945            )
946
947        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
948        indexes = f" {indexes}" if indexes else ""
949        index_sql = indexes + postindex_props_sql
950
951        replace = " OR REPLACE" if expression.args.get("replace") else ""
952        unique = " UNIQUE" if expression.args.get("unique") else ""
953
954        postcreate_props_sql = ""
955        if properties_locs.get(exp.Properties.Location.POST_CREATE):
956            postcreate_props_sql = self.properties(
957                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
958                sep=" ",
959                prefix=" ",
960                wrapped=False,
961            )
962
963        modifiers = "".join((replace, unique, postcreate_props_sql))
964
965        postexpression_props_sql = ""
966        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
967            postexpression_props_sql = self.properties(
968                exp.Properties(
969                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
970                ),
971                sep=" ",
972                prefix=" ",
973                wrapped=False,
974            )
975
976        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
977        no_schema_binding = (
978            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
979        )
980
981        clone = self.sql(expression, "clone")
982        clone = f" {clone}" if clone else ""
983
984        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
985        return self.prepend_ctes(expression, expression_sql)
def sequenceproperties_sql(self, expression: sqlglot.expressions.SequenceProperties) -> str:
 987    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
 988        start = self.sql(expression, "start")
 989        start = f"START WITH {start}" if start else ""
 990        increment = self.sql(expression, "increment")
 991        increment = f" INCREMENT BY {increment}" if increment else ""
 992        minvalue = self.sql(expression, "minvalue")
 993        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 994        maxvalue = self.sql(expression, "maxvalue")
 995        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 996        owned = self.sql(expression, "owned")
 997        owned = f" OWNED BY {owned}" if owned else ""
 998
 999        cache = expression.args.get("cache")
1000        if cache is None:
1001            cache_str = ""
1002        elif cache is True:
1003            cache_str = " CACHE"
1004        else:
1005            cache_str = f" CACHE {cache}"
1006
1007        options = self.expressions(expression, key="options", flat=True, sep=" ")
1008        options = f" {options}" if options else ""
1009
1010        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
1012    def clone_sql(self, expression: exp.Clone) -> str:
1013        this = self.sql(expression, "this")
1014        shallow = "SHALLOW " if expression.args.get("shallow") else ""
1015        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
1016        return f"{shallow}{keyword} {this}"
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
1018    def describe_sql(self, expression: exp.Describe) -> str:
1019        extended = " EXTENDED" if expression.args.get("extended") else ""
1020        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str:
1022    def heredoc_sql(self, expression: exp.Heredoc) -> str:
1023        tag = self.sql(expression, "tag")
1024        return f"${tag}${self.sql(expression, 'this')}${tag}$"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
1026    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
1027        with_ = self.sql(expression, "with")
1028        if with_:
1029            sql = f"{with_}{self.sep()}{sql}"
1030        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
1032    def with_sql(self, expression: exp.With) -> str:
1033        sql = self.expressions(expression, flat=True)
1034        recursive = (
1035            "RECURSIVE "
1036            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
1037            else ""
1038        )
1039
1040        return f"WITH {recursive}{sql}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
1042    def cte_sql(self, expression: exp.CTE) -> str:
1043        alias = self.sql(expression, "alias")
1044        return f"{alias} AS {self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
1046    def tablealias_sql(self, expression: exp.TableAlias) -> str:
1047        alias = self.sql(expression, "this")
1048        columns = self.expressions(expression, key="columns", flat=True)
1049        columns = f"({columns})" if columns else ""
1050
1051        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
1052            columns = ""
1053            self.unsupported("Named columns are not supported in table alias.")
1054
1055        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1056            alias = "_t"
1057
1058        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
1060    def bitstring_sql(self, expression: exp.BitString) -> str:
1061        this = self.sql(expression, "this")
1062        if self.dialect.BIT_START:
1063            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1064        return f"{int(this, 2)}"
def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str:
1066    def hexstring_sql(self, expression: exp.HexString) -> str:
1067        this = self.sql(expression, "this")
1068        if self.dialect.HEX_START:
1069            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1070        return f"{int(this, 16)}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
1072    def bytestring_sql(self, expression: exp.ByteString) -> str:
1073        this = self.sql(expression, "this")
1074        if self.dialect.BYTE_START:
1075            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1076        return this
def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str:
1078    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1079        this = self.sql(expression, "this")
1080        escape = expression.args.get("escape")
1081
1082        if self.dialect.UNICODE_START:
1083            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
1084            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
1085
1086        if escape:
1087            pattern = re.compile(rf"{escape.name}(\d+)")
1088        else:
1089            pattern = ESCAPED_UNICODE_RE
1090
1091        this = pattern.sub(r"\\u\1", this)
1092        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
1094    def rawstring_sql(self, expression: exp.RawString) -> str:
1095        string = self.escape_str(expression.this.replace("\\", "\\\\"))
1096        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str:
1098    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1099        this = self.sql(expression, "this")
1100        specifier = self.sql(expression, "expression")
1101        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1102        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
1104    def datatype_sql(self, expression: exp.DataType) -> str:
1105        type_value = expression.this
1106
1107        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1108            type_sql = self.sql(expression, "kind")
1109        else:
1110            type_sql = (
1111                self.TYPE_MAPPING.get(type_value, type_value.value)
1112                if isinstance(type_value, exp.DataType.Type)
1113                else type_value
1114            )
1115
1116        nested = ""
1117        interior = self.expressions(expression, flat=True)
1118        values = ""
1119
1120        if interior:
1121            if expression.args.get("nested"):
1122                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1123                if expression.args.get("values") is not None:
1124                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1125                    values = self.expressions(expression, key="values", flat=True)
1126                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1127            elif type_value == exp.DataType.Type.INTERVAL:
1128                nested = f" {interior}"
1129            else:
1130                nested = f"({interior})"
1131
1132        type_sql = f"{type_sql}{nested}{values}"
1133        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1134            exp.DataType.Type.TIMETZ,
1135            exp.DataType.Type.TIMESTAMPTZ,
1136        ):
1137            type_sql = f"{type_sql} WITH TIME ZONE"
1138
1139        return type_sql
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
1141    def directory_sql(self, expression: exp.Directory) -> str:
1142        local = "LOCAL " if expression.args.get("local") else ""
1143        row_format = self.sql(expression, "row_format")
1144        row_format = f" {row_format}" if row_format else ""
1145        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
1147    def delete_sql(self, expression: exp.Delete) -> str:
1148        this = self.sql(expression, "this")
1149        this = f" FROM {this}" if this else ""
1150        using = self.sql(expression, "using")
1151        using = f" USING {using}" if using else ""
1152        where = self.sql(expression, "where")
1153        returning = self.sql(expression, "returning")
1154        limit = self.sql(expression, "limit")
1155        tables = self.expressions(expression, key="tables")
1156        tables = f" {tables}" if tables else ""
1157        if self.RETURNING_END:
1158            expression_sql = f"{this}{using}{where}{returning}{limit}"
1159        else:
1160            expression_sql = f"{returning}{this}{using}{where}{limit}"
1161        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
1163    def drop_sql(self, expression: exp.Drop) -> str:
1164        this = self.sql(expression, "this")
1165        kind = expression.args["kind"]
1166        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1167        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1168        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1169        cascade = " CASCADE" if expression.args.get("cascade") else ""
1170        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1171        purge = " PURGE" if expression.args.get("purge") else ""
1172        return (
1173            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
1174        )
def except_sql(self, expression: sqlglot.expressions.Except) -> str:
1176    def except_sql(self, expression: exp.Except) -> str:
1177        return self.set_operations(expression)
def except_op(self, expression: sqlglot.expressions.Except) -> str:
1179    def except_op(self, expression: exp.Except) -> str:
1180        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
1182    def fetch_sql(self, expression: exp.Fetch) -> str:
1183        direction = expression.args.get("direction")
1184        direction = f" {direction}" if direction else ""
1185        count = expression.args.get("count")
1186        count = f" {count}" if count else ""
1187        if expression.args.get("percent"):
1188            count = f"{count} PERCENT"
1189        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
1190        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
1192    def filter_sql(self, expression: exp.Filter) -> str:
1193        if self.AGGREGATE_FILTER_SUPPORTED:
1194            this = self.sql(expression, "this")
1195            where = self.sql(expression, "expression").strip()
1196            return f"{this} FILTER({where})"
1197
1198        agg = expression.this
1199        agg_arg = agg.this
1200        cond = expression.expression.this
1201        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1202        return self.sql(agg)
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
1204    def hint_sql(self, expression: exp.Hint) -> str:
1205        if not self.QUERY_HINTS:
1206            self.unsupported("Hints are not supported")
1207            return ""
1208
1209        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
1211    def index_sql(self, expression: exp.Index) -> str:
1212        unique = "UNIQUE " if expression.args.get("unique") else ""
1213        primary = "PRIMARY " if expression.args.get("primary") else ""
1214        amp = "AMP " if expression.args.get("amp") else ""
1215        name = self.sql(expression, "this")
1216        name = f"{name} " if name else ""
1217        table = self.sql(expression, "table")
1218        table = f"{self.INDEX_ON} {table}" if table else ""
1219        using = self.sql(expression, "using")
1220        using = f" USING {using}" if using else ""
1221        index = "INDEX " if not table else ""
1222        columns = self.expressions(expression, key="columns", flat=True)
1223        columns = f"({columns})" if columns else ""
1224        partition_by = self.expressions(expression, key="partition_by", flat=True)
1225        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1226        where = self.sql(expression, "where")
1227        include = self.expressions(expression, key="include", flat=True)
1228        if include:
1229            include = f" INCLUDE ({include})"
1230        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
1232    def identifier_sql(self, expression: exp.Identifier) -> str:
1233        text = expression.name
1234        lower = text.lower()
1235        text = lower if self.normalize and not expression.quoted else text
1236        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
1237        if (
1238            expression.quoted
1239            or self.dialect.can_identify(text, self.identify)
1240            or lower in self.RESERVED_KEYWORDS
1241            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1242        ):
1243            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
1244        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
1246    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1247        input_format = self.sql(expression, "input_format")
1248        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1249        output_format = self.sql(expression, "output_format")
1250        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1251        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
1253    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1254        string = self.sql(exp.Literal.string(expression.name))
1255        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
1257    def partition_sql(self, expression: exp.Partition) -> str:
1258        return f"PARTITION({self.expressions(expression, flat=True)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
1260    def properties_sql(self, expression: exp.Properties) -> str:
1261        root_properties = []
1262        with_properties = []
1263
1264        for p in expression.expressions:
1265            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1266            if p_loc == exp.Properties.Location.POST_WITH:
1267                with_properties.append(p)
1268            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1269                root_properties.append(p)
1270
1271        return self.root_properties(
1272            exp.Properties(expressions=root_properties)
1273        ) + self.with_properties(exp.Properties(expressions=with_properties))
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
1275    def root_properties(self, properties: exp.Properties) -> str:
1276        if properties.expressions:
1277            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1278        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1280    def properties(
1281        self,
1282        properties: exp.Properties,
1283        prefix: str = "",
1284        sep: str = ", ",
1285        suffix: str = "",
1286        wrapped: bool = True,
1287    ) -> str:
1288        if properties.expressions:
1289            expressions = self.expressions(properties, sep=sep, indent=False)
1290            if expressions:
1291                expressions = self.wrap(expressions) if wrapped else expressions
1292                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1293        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
1295    def with_properties(self, properties: exp.Properties) -> str:
1296        return self.properties(properties, prefix=self.seg("WITH"))
def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict:
1298    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1299        properties_locs = defaultdict(list)
1300        for p in properties.expressions:
1301            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1302            if p_loc != exp.Properties.Location.UNSUPPORTED:
1303                properties_locs[p_loc].append(p)
1304            else:
1305                self.unsupported(f"Unsupported property {p.key}")
1306
1307        return properties_locs
def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1309    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1310        if isinstance(expression.this, exp.Dot):
1311            return self.sql(expression, "this")
1312        return f"'{expression.name}'" if string_key else expression.name
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
1314    def property_sql(self, expression: exp.Property) -> str:
1315        property_cls = expression.__class__
1316        if property_cls == exp.Property:
1317            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1318
1319        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1320        if not property_name:
1321            self.unsupported(f"Unsupported property {expression.key}")
1322
1323        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
1325    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1326        if self.SUPPORTS_CREATE_TABLE_LIKE:
1327            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1328            options = f" {options}" if options else ""
1329
1330            like = f"LIKE {self.sql(expression, 'this')}{options}"
1331            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1332                like = f"({like})"
1333
1334            return like
1335
1336        if expression.expressions:
1337            self.unsupported("Transpilation of LIKE property options is unsupported")
1338
1339        select = exp.select("*").from_(expression.this).limit(0)
1340        return f"AS {self.sql(select)}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
1342    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1343        no = "NO " if expression.args.get("no") else ""
1344        protection = " PROTECTION" if expression.args.get("protection") else ""
1345        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
1347    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1348        no = "NO " if expression.args.get("no") else ""
1349        local = expression.args.get("local")
1350        local = f"{local} " if local else ""
1351        dual = "DUAL " if expression.args.get("dual") else ""
1352        before = "BEFORE " if expression.args.get("before") else ""
1353        after = "AFTER " if expression.args.get("after") else ""
1354        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1356    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1357        freespace = self.sql(expression, "this")
1358        percent = " PERCENT" if expression.args.get("percent") else ""
1359        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1361    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1362        if expression.args.get("default"):
1363            property = "DEFAULT"
1364        elif expression.args.get("on"):
1365            property = "ON"
1366        else:
1367            property = "OFF"
1368        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1370    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1371        if expression.args.get("no"):
1372            return "NO MERGEBLOCKRATIO"
1373        if expression.args.get("default"):
1374            return "DEFAULT MERGEBLOCKRATIO"
1375
1376        percent = " PERCENT" if expression.args.get("percent") else ""
1377        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1379    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1380        default = expression.args.get("default")
1381        minimum = expression.args.get("minimum")
1382        maximum = expression.args.get("maximum")
1383        if default or minimum or maximum:
1384            if default:
1385                prop = "DEFAULT"
1386            elif minimum:
1387                prop = "MINIMUM"
1388            else:
1389                prop = "MAXIMUM"
1390            return f"{prop} DATABLOCKSIZE"
1391        units = expression.args.get("units")
1392        units = f" {units}" if units else ""
1393        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1395    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1396        autotemp = expression.args.get("autotemp")
1397        always = expression.args.get("always")
1398        default = expression.args.get("default")
1399        manual = expression.args.get("manual")
1400        never = expression.args.get("never")
1401
1402        if autotemp is not None:
1403            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1404        elif always:
1405            prop = "ALWAYS"
1406        elif default:
1407            prop = "DEFAULT"
1408        elif manual:
1409            prop = "MANUAL"
1410        elif never:
1411            prop = "NEVER"
1412        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1414    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1415        no = expression.args.get("no")
1416        no = " NO" if no else ""
1417        concurrent = expression.args.get("concurrent")
1418        concurrent = " CONCURRENT" if concurrent else ""
1419
1420        for_ = ""
1421        if expression.args.get("for_all"):
1422            for_ = " FOR ALL"
1423        elif expression.args.get("for_insert"):
1424            for_ = " FOR INSERT"
1425        elif expression.args.get("for_none"):
1426            for_ = " FOR NONE"
1427        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str:
1429    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1430        if isinstance(expression.this, list):
1431            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1432        if expression.this:
1433            modulus = self.sql(expression, "this")
1434            remainder = self.sql(expression, "expression")
1435            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1436
1437        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1438        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1439        return f"FROM ({from_expressions}) TO ({to_expressions})"
def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str:
1441    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1442        this = self.sql(expression, "this")
1443
1444        for_values_or_default = expression.expression
1445        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1446            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1447        else:
1448            for_values_or_default = " DEFAULT"
1449
1450        return f"PARTITION OF {this}{for_values_or_default}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1452    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1453        kind = expression.args.get("kind")
1454        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1455        for_or_in = expression.args.get("for_or_in")
1456        for_or_in = f" {for_or_in}" if for_or_in else ""
1457        lock_type = expression.args.get("lock_type")
1458        override = " OVERRIDE" if expression.args.get("override") else ""
1459        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1461    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1462        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1463        statistics = expression.args.get("statistics")
1464        statistics_sql = ""
1465        if statistics is not None:
1466            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1467        return f"{data_sql}{statistics_sql}"
def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1469    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1470        sql = "WITH(SYSTEM_VERSIONING=ON"
1471
1472        if expression.this:
1473            history_table = self.sql(expression, "this")
1474            sql = f"{sql}(HISTORY_TABLE={history_table}"
1475
1476            if expression.expression:
1477                data_consistency_check = self.sql(expression, "expression")
1478                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
1479
1480            sql = f"{sql})"
1481
1482        return f"{sql})"
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1484    def insert_sql(self, expression: exp.Insert) -> str:
1485        hint = self.sql(expression, "hint")
1486        overwrite = expression.args.get("overwrite")
1487
1488        if isinstance(expression.this, exp.Directory):
1489            this = " OVERWRITE" if overwrite else " INTO"
1490        else:
1491            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1492
1493        alternative = expression.args.get("alternative")
1494        alternative = f" OR {alternative}" if alternative else ""
1495        ignore = " IGNORE" if expression.args.get("ignore") else ""
1496
1497        this = f"{this} {self.sql(expression, 'this')}"
1498
1499        exists = " IF EXISTS" if expression.args.get("exists") else ""
1500        partition_sql = (
1501            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1502        )
1503        where = self.sql(expression, "where")
1504        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1505        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1506        on_conflict = self.sql(expression, "conflict")
1507        on_conflict = f" {on_conflict}" if on_conflict else ""
1508        by_name = " BY NAME" if expression.args.get("by_name") else ""
1509        returning = self.sql(expression, "returning")
1510
1511        if self.RETURNING_END:
1512            expression_sql = f"{expression_sql}{on_conflict}{returning}"
1513        else:
1514            expression_sql = f"{returning}{expression_sql}{on_conflict}"
1515
1516        sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1517        return self.prepend_ctes(expression, sql)
def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str:
1519    def intersect_sql(self, expression: exp.Intersect) -> str:
1520        return self.set_operations(expression)
def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str:
1522    def intersect_op(self, expression: exp.Intersect) -> str:
1523        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1525    def introducer_sql(self, expression: exp.Introducer) -> str:
1526        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def kill_sql(self, expression: sqlglot.expressions.Kill) -> str:
1528    def kill_sql(self, expression: exp.Kill) -> str:
1529        kind = self.sql(expression, "kind")
1530        kind = f" {kind}" if kind else ""
1531        this = self.sql(expression, "this")
1532        this = f" {this}" if this else ""
1533        return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1535    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1536        return expression.name
def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str:
1538    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1539        return expression.name
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1541    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1542        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1543
1544        constraint = self.sql(expression, "constraint")
1545        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
1546
1547        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
1548        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
1549        action = self.sql(expression, "action")
1550
1551        expressions = self.expressions(expression, flat=True)
1552        if expressions:
1553            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1554            expressions = f" {set_keyword}{expressions}"
1555
1556        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1558    def returning_sql(self, expression: exp.Returning) -> str:
1559        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1561    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1562        fields = expression.args.get("fields")
1563        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1564        escaped = expression.args.get("escaped")
1565        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1566        items = expression.args.get("collection_items")
1567        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1568        keys = expression.args.get("map_keys")
1569        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1570        lines = expression.args.get("lines")
1571        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1572        null = expression.args.get("null")
1573        null = f" NULL DEFINED AS {null}" if null else ""
1574        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str:
1576    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1577        return f"WITH ({self.expressions(expression, flat=True)})"
def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str:
1579    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1580        this = f"{self.sql(expression, 'this')} INDEX"
1581        target = self.sql(expression, "target")
1582        target = f" FOR {target}" if target else ""
1583        return f"{this}{target} ({self.expressions(expression, flat=True)})"
def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str:
1585    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1586        this = self.sql(expression, "this")
1587        kind = self.sql(expression, "kind")
1588        expr = self.sql(expression, "expression")
1589        return f"{this} ({kind} => {expr})"
def table_parts(self, expression: sqlglot.expressions.Table) -> str:
1591    def table_parts(self, expression: exp.Table) -> str:
1592        return ".".join(
1593            self.sql(part)
1594            for part in (
1595                expression.args.get("catalog"),
1596                expression.args.get("db"),
1597                expression.args.get("this"),
1598            )
1599            if part is not None
1600        )
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
1602    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1603        table = self.table_parts(expression)
1604        only = "ONLY " if expression.args.get("only") else ""
1605        version = self.sql(expression, "version")
1606        version = f" {version}" if version else ""
1607        alias = self.sql(expression, "alias")
1608        alias = f"{sep}{alias}" if alias else ""
1609        hints = self.expressions(expression, key="hints", sep=" ")
1610        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1611        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1612        pivots = f" {pivots}" if pivots else ""
1613        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1614        laterals = self.expressions(expression, key="laterals", sep="")
1615
1616        file_format = self.sql(expression, "format")
1617        if file_format:
1618            pattern = self.sql(expression, "pattern")
1619            pattern = f", PATTERN => {pattern}" if pattern else ""
1620            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1621
1622        ordinality = expression.args.get("ordinality") or ""
1623        if ordinality:
1624            ordinality = f" WITH ORDINALITY{alias}"
1625            alias = ""
1626
1627        when = self.sql(expression, "when")
1628        if when:
1629            table = f"{table} {when}"
1630
1631        return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
def tablesample_sql( self, expression: sqlglot.expressions.TableSample, sep: str = ' AS ', tablesample_keyword: Optional[str] = None) -> str:
1633    def tablesample_sql(
1634        self,
1635        expression: exp.TableSample,
1636        sep: str = " AS ",
1637        tablesample_keyword: t.Optional[str] = None,
1638    ) -> str:
1639        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1640            table = expression.this.copy()
1641            table.set("alias", None)
1642            this = self.sql(table)
1643            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1644        else:
1645            this = self.sql(expression, "this")
1646            alias = ""
1647
1648        method = self.sql(expression, "method")
1649        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1650        numerator = self.sql(expression, "bucket_numerator")
1651        denominator = self.sql(expression, "bucket_denominator")
1652        field = self.sql(expression, "bucket_field")
1653        field = f" ON {field}" if field else ""
1654        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1655        seed = self.sql(expression, "seed")
1656        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1657
1658        size = self.sql(expression, "size")
1659        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1660            size = f"{size} ROWS"
1661
1662        percent = self.sql(expression, "percent")
1663        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1664            percent = f"{percent} PERCENT"
1665
1666        expr = f"{bucket}{percent}{size}"
1667        if self.TABLESAMPLE_REQUIRES_PARENS:
1668            expr = f"({expr})"
1669
1670        return (
1671            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1672        )
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
1674    def pivot_sql(self, expression: exp.Pivot) -> str:
1675        expressions = self.expressions(expression, flat=True)
1676
1677        if expression.this:
1678            this = self.sql(expression, "this")
1679            if not expressions:
1680                return f"UNPIVOT {this}"
1681
1682            on = f"{self.seg('ON')} {expressions}"
1683            using = self.expressions(expression, key="using", flat=True)
1684            using = f"{self.seg('USING')} {using}" if using else ""
1685            group = self.sql(expression, "group")
1686            return f"PIVOT {this}{on}{using}{group}"
1687
1688        alias = self.sql(expression, "alias")
1689        alias = f" AS {alias}" if alias else ""
1690        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1691        field = self.sql(expression, "field")
1692        include_nulls = expression.args.get("include_nulls")
1693        if include_nulls is not None:
1694            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1695        else:
1696            nulls = ""
1697        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
def version_sql(self, expression: sqlglot.expressions.Version) -> str:
1699    def version_sql(self, expression: exp.Version) -> str:
1700        this = f"FOR {expression.name}"
1701        kind = expression.text("kind")
1702        expr = self.sql(expression, "expression")
1703        return f"{this} {kind} {expr}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
1705    def tuple_sql(self, expression: exp.Tuple) -> str:
1706        return f"({self.expressions(expression, flat=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
1708    def update_sql(self, expression: exp.Update) -> str:
1709        this = self.sql(expression, "this")
1710        set_sql = self.expressions(expression, flat=True)
1711        from_sql = self.sql(expression, "from")
1712        where_sql = self.sql(expression, "where")
1713        returning = self.sql(expression, "returning")
1714        order = self.sql(expression, "order")
1715        limit = self.sql(expression, "limit")
1716        if self.RETURNING_END:
1717            expression_sql = f"{from_sql}{where_sql}{returning}"
1718        else:
1719            expression_sql = f"{returning}{from_sql}{where_sql}"
1720        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1721        return self.prepend_ctes(expression, sql)
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
1723    def values_sql(self, expression: exp.Values) -> str:
1724        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1725        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1726            args = self.expressions(expression)
1727            alias = self.sql(expression, "alias")
1728            values = f"VALUES{self.seg('')}{args}"
1729            values = (
1730                f"({values})"
1731                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1732                else values
1733            )
1734            return f"{values} AS {alias}" if alias else values
1735
1736        # Converts `VALUES...` expression into a series of select unions.
1737        alias_node = expression.args.get("alias")
1738        column_names = alias_node and alias_node.columns
1739
1740        selects: t.List[exp.Query] = []
1741
1742        for i, tup in enumerate(expression.expressions):
1743            row = tup.expressions
1744
1745            if i == 0 and column_names:
1746                row = [
1747                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1748                ]
1749
1750            selects.append(exp.Select(expressions=row))
1751
1752        if self.pretty:
1753            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1754            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1755            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1756            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1757            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
1758
1759        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
1760        unions = " UNION ALL ".join(self.sql(select) for select in selects)
1761        return f"({unions}){alias}"
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
1763    def var_sql(self, expression: exp.Var) -> str:
1764        return self.sql(expression, "this")
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
1766    def into_sql(self, expression: exp.Into) -> str:
1767        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1768        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1769        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
1771    def from_sql(self, expression: exp.From) -> str:
1772        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
1774    def group_sql(self, expression: exp.Group) -> str:
1775        group_by = self.op_expressions("GROUP BY", expression)
1776
1777        if expression.args.get("all"):
1778            return f"{group_by} ALL"
1779
1780        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1781        grouping_sets = (
1782            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1783        )
1784
1785        cube = expression.args.get("cube", [])
1786        if seq_get(cube, 0) is True:
1787            return f"{group_by}{self.seg('WITH CUBE')}"
1788        else:
1789            cube_sql = self.expressions(expression, key="cube", indent=False)
1790            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1791
1792        rollup = expression.args.get("rollup", [])
1793        if seq_get(rollup, 0) is True:
1794            return f"{group_by}{self.seg('WITH ROLLUP')}"
1795        else:
1796            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1797            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1798
1799        groupings = csv(
1800            grouping_sets,
1801            cube_sql,
1802            rollup_sql,
1803            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
1804            sep=self.GROUPINGS_SEP,
1805        )
1806
1807        if expression.args.get("expressions") and groupings:
1808            group_by = f"{group_by}{self.GROUPINGS_SEP}"
1809
1810        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
1812    def having_sql(self, expression: exp.Having) -> str:
1813        this = self.indent(self.sql(expression, "this"))
1814        return f"{self.seg('HAVING')}{self.sep()}{this}"
def connect_sql(self, expression: sqlglot.expressions.Connect) -> str:
1816    def connect_sql(self, expression: exp.Connect) -> str:
1817        start = self.sql(expression, "start")
1818        start = self.seg(f"START WITH {start}") if start else ""
1819        connect = self.sql(expression, "connect")
1820        connect = self.seg(f"CONNECT BY {connect}")
1821        return start + connect
def prior_sql(self, expression: sqlglot.expressions.Prior) -> str:
1823    def prior_sql(self, expression: exp.Prior) -> str:
1824        return f"PRIOR {self.sql(expression, 'this')}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
1826    def join_sql(self, expression: exp.Join) -> str:
1827        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
1828            side = None
1829        else:
1830            side = expression.side
1831
1832        op_sql = " ".join(
1833            op
1834            for op in (
1835                expression.method,
1836                "GLOBAL" if expression.args.get("global") else None,
1837                side,
1838                expression.kind,
1839                expression.hint if self.JOIN_HINTS else None,
1840            )
1841            if op
1842        )
1843        on_sql = self.sql(expression, "on")
1844        using = expression.args.get("using")
1845
1846        if not on_sql and using:
1847            on_sql = csv(*(self.sql(column) for column in using))
1848
1849        this = expression.this
1850        this_sql = self.sql(this)
1851
1852        if on_sql:
1853            on_sql = self.indent(on_sql, skip_first=True)
1854            space = self.seg(" " * self.pad) if self.pretty else " "
1855            if using:
1856                on_sql = f"{space}USING ({on_sql})"
1857            else:
1858                on_sql = f"{space}ON {on_sql}"
1859        elif not op_sql:
1860            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
1861                return f" {this_sql}"
1862
1863            return f", {this_sql}"
1864
1865        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
1866        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str:
1868    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1869        args = self.expressions(expression, flat=True)
1870        args = f"({args})" if len(args.split(",")) > 1 else args
1871        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_op(self, expression: sqlglot.expressions.Lateral) -> str:
1873    def lateral_op(self, expression: exp.Lateral) -> str:
1874        cross_apply = expression.args.get("cross_apply")
1875
1876        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
1877        if cross_apply is True:
1878            op = "INNER JOIN "
1879        elif cross_apply is False:
1880            op = "LEFT JOIN "
1881        else:
1882            op = ""
1883
1884        return f"{op}LATERAL"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
1886    def lateral_sql(self, expression: exp.Lateral) -> str:
1887        this = self.sql(expression, "this")
1888
1889        if expression.args.get("view"):
1890            alias = expression.args["alias"]
1891            columns = self.expressions(alias, key="columns", flat=True)
1892            table = f" {alias.name}" if alias.name else ""
1893            columns = f" AS {columns}" if columns else ""
1894            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1895            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1896
1897        alias = self.sql(expression, "alias")
1898        alias = f" AS {alias}" if alias else ""
1899        return f"{self.lateral_op(expression)} {this}{alias}"
def limit_sql(self, expression: sqlglot.expressions.Limit, top: bool = False) -> str:
1901    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
1902        this = self.sql(expression, "this")
1903
1904        args = [
1905            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
1906            for e in (expression.args.get(k) for k in ("offset", "expression"))
1907            if e
1908        ]
1909
1910        args_sql = ", ".join(self.sql(e) for e in args)
1911        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
1912        expressions = self.expressions(expression, flat=True)
1913        expressions = f" BY {expressions}" if expressions else ""
1914
1915        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
1917    def offset_sql(self, expression: exp.Offset) -> str:
1918        this = self.sql(expression, "this")
1919        value = expression.expression
1920        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
1921        expressions = self.expressions(expression, flat=True)
1922        expressions = f" BY {expressions}" if expressions else ""
1923        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str:
1925    def setitem_sql(self, expression: exp.SetItem) -> str:
1926        kind = self.sql(expression, "kind")
1927        kind = f"{kind} " if kind else ""
1928        this = self.sql(expression, "this")
1929        expressions = self.expressions(expression)
1930        collate = self.sql(expression, "collate")
1931        collate = f" COLLATE {collate}" if collate else ""
1932        global_ = "GLOBAL " if expression.args.get("global") else ""
1933        return f"{global_}{kind}{this}{expressions}{collate}"
def set_sql(self, expression: sqlglot.expressions.Set) -> str:
1935    def set_sql(self, expression: exp.Set) -> str:
1936        expressions = (
1937            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
1938        )
1939        tag = " TAG" if expression.args.get("tag") else ""
1940        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str:
1942    def pragma_sql(self, expression: exp.Pragma) -> str:
1943        return f"PRAGMA {self.sql(expression, 'this')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
1945    def lock_sql(self, expression: exp.Lock) -> str:
1946        if not self.LOCKING_READS_SUPPORTED:
1947            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1948            return ""
1949
1950        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
1951        expressions = self.expressions(expression, flat=True)
1952        expressions = f" OF {expressions}" if expressions else ""
1953        wait = expression.args.get("wait")
1954
1955        if wait is not None:
1956            if isinstance(wait, exp.Literal):
1957                wait = f" WAIT {self.sql(wait)}"
1958            else:
1959                wait = " NOWAIT" if wait else " SKIP LOCKED"
1960
1961        return f"{lock_type}{expressions}{wait or ''}"
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
1963    def literal_sql(self, expression: exp.Literal) -> str:
1964        text = expression.this or ""
1965        if expression.is_string:
1966            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
1967        return text
def escape_str(self, text: str) -> str:
1969    def escape_str(self, text: str) -> str:
1970        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
1971        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
1972            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
1973        elif self.pretty:
1974            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1975        return text
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
1977    def loaddata_sql(self, expression: exp.LoadData) -> str:
1978        local = " LOCAL" if expression.args.get("local") else ""
1979        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1980        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1981        this = f" INTO TABLE {self.sql(expression, 'this')}"
1982        partition = self.sql(expression, "partition")
1983        partition = f" {partition}" if partition else ""
1984        input_format = self.sql(expression, "input_format")
1985        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1986        serde = self.sql(expression, "serde")
1987        serde = f" SERDE {serde}" if serde else ""
1988        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
1990    def null_sql(self, *_) -> str:
1991        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
1993    def boolean_sql(self, expression: exp.Boolean) -> str:
1994        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
1996    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1997        this = self.sql(expression, "this")
1998        this = f"{this} " if this else this
1999        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
2000        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
2001        interpolated_values = [
2002            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
2003            for named_expression in expression.args.get("interpolate") or []
2004        ]
2005        interpolate = (
2006            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
2007        )
2008        return f"{order}{interpolate}"
def withfill_sql(self, expression: sqlglot.expressions.WithFill) -> str:
2010    def withfill_sql(self, expression: exp.WithFill) -> str:
2011        from_sql = self.sql(expression, "from")
2012        from_sql = f" FROM {from_sql}" if from_sql else ""
2013        to_sql = self.sql(expression, "to")
2014        to_sql = f" TO {to_sql}" if to_sql else ""
2015        step_sql = self.sql(expression, "step")
2016        step_sql = f" STEP {step_sql}" if step_sql else ""
2017        return f"WITH FILL{from_sql}{to_sql}{step_sql}"
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
2019    def cluster_sql(self, expression: exp.Cluster) -> str:
2020        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
2022    def distribute_sql(self, expression: exp.Distribute) -> str:
2023        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
2025    def sort_sql(self, expression: exp.Sort) -> str:
2026        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
2028    def ordered_sql(self, expression: exp.Ordered) -> str:
2029        desc = expression.args.get("desc")
2030        asc = not desc
2031
2032        nulls_first = expression.args.get("nulls_first")
2033        nulls_last = not nulls_first
2034        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
2035        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
2036        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
2037
2038        this = self.sql(expression, "this")
2039
2040        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
2041        nulls_sort_change = ""
2042        if nulls_first and (
2043            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
2044        ):
2045            nulls_sort_change = " NULLS FIRST"
2046        elif (
2047            nulls_last
2048            and ((asc and nulls_are_small) or (desc and nulls_are_large))
2049            and not nulls_are_last
2050        ):
2051            nulls_sort_change = " NULLS LAST"
2052
2053        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2054        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2055            window = expression.find_ancestor(exp.Window, exp.Select)
2056            if isinstance(window, exp.Window) and window.args.get("spec"):
2057                self.unsupported(
2058                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2059                )
2060                nulls_sort_change = ""
2061            elif self.NULL_ORDERING_SUPPORTED is None:
2062                if expression.this.is_int:
2063                    self.unsupported(
2064                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2065                    )
2066                else:
2067                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2068                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2069                nulls_sort_change = ""
2070
2071        with_fill = self.sql(expression, "with_fill")
2072        with_fill = f" {with_fill}" if with_fill else ""
2073
2074        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
2076    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2077        partition = self.partition_by_sql(expression)
2078        order = self.sql(expression, "order")
2079        measures = self.expressions(expression, key="measures")
2080        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2081        rows = self.sql(expression, "rows")
2082        rows = self.seg(rows) if rows else ""
2083        after = self.sql(expression, "after")
2084        after = self.seg(after) if after else ""
2085        pattern = self.sql(expression, "pattern")
2086        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2087        definition_sqls = [
2088            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2089            for definition in expression.args.get("define", [])
2090        ]
2091        definitions = self.expressions(sqls=definition_sqls)
2092        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2093        body = "".join(
2094            (
2095                partition,
2096                order,
2097                measures,
2098                rows,
2099                after,
2100                pattern,
2101                define,
2102            )
2103        )
2104        alias = self.sql(expression, "alias")
2105        alias = f" {alias}" if alias else ""
2106        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
2108    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2109        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
2110
2111        # If the limit is generated as TOP, we need to ensure it's not generated twice
2112        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
2113
2114        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2115            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2116        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2117            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2118
2119        fetch = isinstance(limit, exp.Fetch)
2120
2121        offset_limit_modifiers = (
2122            self.offset_limit_modifiers(expression, fetch, limit)
2123            if with_offset_limit_modifiers
2124            else []
2125        )
2126
2127        options = self.expressions(expression, key="options")
2128        if options:
2129            options = f" OPTION{self.wrap(options)}"
2130
2131        return csv(
2132            *sqls,
2133            *[self.sql(join) for join in expression.args.get("joins") or []],
2134            self.sql(expression, "connect"),
2135            self.sql(expression, "match"),
2136            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2137            self.sql(expression, "prewhere"),
2138            self.sql(expression, "where"),
2139            self.sql(expression, "group"),
2140            self.sql(expression, "having"),
2141            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
2142            self.sql(expression, "order"),
2143            *offset_limit_modifiers,
2144            *self.after_limit_modifiers(expression),
2145            options,
2146            sep="",
2147        )
def queryoption_sql(self, expression: sqlglot.expressions.QueryOption) -> str:
2149    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2150        return ""
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2152    def offset_limit_modifiers(
2153        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2154    ) -> t.List[str]:
2155        return [
2156            self.sql(expression, "offset") if fetch else self.sql(limit),
2157            self.sql(limit) if fetch else self.sql(expression, "offset"),
2158        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2160    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2161        locks = self.expressions(expression, key="locks", sep=" ")
2162        locks = f" {locks}" if locks else ""
2163        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
2165    def select_sql(self, expression: exp.Select) -> str:
2166        into = expression.args.get("into")
2167        if not self.SUPPORTS_SELECT_INTO and into:
2168            into.pop()
2169
2170        hint = self.sql(expression, "hint")
2171        distinct = self.sql(expression, "distinct")
2172        distinct = f" {distinct}" if distinct else ""
2173        kind = self.sql(expression, "kind")
2174        limit = expression.args.get("limit")
2175        top = (
2176            self.limit_sql(limit, top=True)
2177            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2178            else ""
2179        )
2180
2181        expressions = self.expressions(expression)
2182
2183        if kind:
2184            if kind in self.SELECT_KINDS:
2185                kind = f" AS {kind}"
2186            else:
2187                if kind == "STRUCT":
2188                    expressions = self.expressions(
2189                        sqls=[
2190                            self.sql(
2191                                exp.Struct(
2192                                    expressions=[
2193                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2194                                        if isinstance(e, exp.Alias)
2195                                        else e
2196                                        for e in expression.expressions
2197                                    ]
2198                                )
2199                            )
2200                        ]
2201                    )
2202                kind = ""
2203
2204        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2205        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2206        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2207        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2208        sql = self.query_modifiers(
2209            expression,
2210            f"SELECT{top_distinct}{kind}{expressions}",
2211            self.sql(expression, "into", comment=False),
2212            self.sql(expression, "from", comment=False),
2213        )
2214
2215        sql = self.prepend_ctes(expression, sql)
2216
2217        if not self.SUPPORTS_SELECT_INTO and into:
2218            if into.args.get("temporary"):
2219                table_kind = " TEMPORARY"
2220            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2221                table_kind = " UNLOGGED"
2222            else:
2223                table_kind = ""
2224            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2225
2226        return sql
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
2228    def schema_sql(self, expression: exp.Schema) -> str:
2229        this = self.sql(expression, "this")
2230        sql = self.schema_columns_sql(expression)
2231        return f"{this} {sql}" if this and sql else this or sql
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
2233    def schema_columns_sql(self, expression: exp.Schema) -> str:
2234        if expression.expressions:
2235            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2236        return ""
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
2238    def star_sql(self, expression: exp.Star) -> str:
2239        except_ = self.expressions(expression, key="except", flat=True)
2240        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2241        replace = self.expressions(expression, key="replace", flat=True)
2242        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2243        return f"*{except_}{replace}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
2245    def parameter_sql(self, expression: exp.Parameter) -> str:
2246        this = self.sql(expression, "this")
2247        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
2249    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2250        this = self.sql(expression, "this")
2251        kind = expression.text("kind")
2252        if kind:
2253            kind = f"{kind}."
2254        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
2256    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2257        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
2259    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2260        alias = self.sql(expression, "alias")
2261        alias = f"{sep}{alias}" if alias else ""
2262
2263        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2264        pivots = f" {pivots}" if pivots else ""
2265
2266        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2267        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
2269    def qualify_sql(self, expression: exp.Qualify) -> str:
2270        this = self.indent(self.sql(expression, "this"))
2271        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def set_operations(self, expression: sqlglot.expressions.Union) -> str:
2273    def set_operations(self, expression: exp.Union) -> str:
2274        sqls: t.List[str] = []
2275        stack: t.List[t.Union[str, exp.Expression]] = [expression]
2276
2277        while stack:
2278            node = stack.pop()
2279
2280            if isinstance(node, exp.Union):
2281                stack.append(node.expression)
2282                stack.append(
2283                    self.maybe_comment(
2284                        getattr(self, f"{node.key}_op")(node),
2285                        expression=node.this,
2286                        comments=node.comments,
2287                    )
2288                )
2289                stack.append(node.this)
2290            else:
2291                sqls.append(self.sql(node))
2292
2293        this = self.sep().join(sqls)
2294        this = self.query_modifiers(expression, this)
2295        return self.prepend_ctes(expression, this)
def union_sql(self, expression: sqlglot.expressions.Union) -> str:
2297    def union_sql(self, expression: exp.Union) -> str:
2298        return self.set_operations(expression)
def union_op(self, expression: sqlglot.expressions.Union) -> str:
2300    def union_op(self, expression: exp.Union) -> str:
2301        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2302        kind = kind if expression.args.get("distinct") else " ALL"
2303        by_name = " BY NAME" if expression.args.get("by_name") else ""
2304        return f"UNION{kind}{by_name}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
2306    def unnest_sql(self, expression: exp.Unnest) -> str:
2307        args = self.expressions(expression, flat=True)
2308
2309        alias = expression.args.get("alias")
2310        offset = expression.args.get("offset")
2311
2312        if self.UNNEST_WITH_ORDINALITY:
2313            if alias and isinstance(offset, exp.Expression):
2314                alias.append("columns", offset)
2315
2316        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2317            columns = alias.columns
2318            alias = self.sql(columns[0]) if columns else ""
2319        else:
2320            alias = self.sql(alias)
2321
2322        alias = f" AS {alias}" if alias else alias
2323        if self.UNNEST_WITH_ORDINALITY:
2324            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2325        else:
2326            if isinstance(offset, exp.Expression):
2327                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2328            elif offset:
2329                suffix = f"{alias} WITH OFFSET"
2330            else:
2331                suffix = alias
2332
2333        return f"UNNEST({args}){suffix}"
def prewhere_sql(self, expression: sqlglot.expressions.PreWhere) -> str:
2335    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2336        return ""
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
2338    def where_sql(self, expression: exp.Where) -> str:
2339        this = self.indent(self.sql(expression, "this"))
2340        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
2342    def window_sql(self, expression: exp.Window) -> str:
2343        this = self.sql(expression, "this")
2344        partition = self.partition_by_sql(expression)
2345        order = expression.args.get("order")
2346        order = self.order_sql(order, flat=True) if order else ""
2347        spec = self.sql(expression, "spec")
2348        alias = self.sql(expression, "alias")
2349        over = self.sql(expression, "over") or "OVER"
2350
2351        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2352
2353        first = expression.args.get("first")
2354        if first is None:
2355            first = ""
2356        else:
2357            first = "FIRST" if first else "LAST"
2358
2359        if not partition and not order and not spec and alias:
2360            return f"{this} {alias}"
2361
2362        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2363        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2365    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2366        partition = self.expressions(expression, key="partition_by", flat=True)
2367        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
2369    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2370        kind = self.sql(expression, "kind")
2371        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2372        end = (
2373            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2374            or "CURRENT ROW"
2375        )
2376        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
2378    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2379        this = self.sql(expression, "this")
2380        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2381        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
2383    def between_sql(self, expression: exp.Between) -> str:
2384        this = self.sql(expression, "this")
2385        low = self.sql(expression, "low")
2386        high = self.sql(expression, "high")
2387        return f"{this} BETWEEN {low} AND {high}"
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
2389    def bracket_sql(self, expression: exp.Bracket) -> str:
2390        expressions = apply_index_offset(
2391            expression.this,
2392            expression.expressions,
2393            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2394        )
2395        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2396        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
2398    def all_sql(self, expression: exp.All) -> str:
2399        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
2401    def any_sql(self, expression: exp.Any) -> str:
2402        this = self.sql(expression, "this")
2403        if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2404            this = self.wrap(this)
2405        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
2407    def exists_sql(self, expression: exp.Exists) -> str:
2408        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
2410    def case_sql(self, expression: exp.Case) -> str:
2411        this = self.sql(expression, "this")
2412        statements = [f"CASE {this}" if this else "CASE"]
2413
2414        for e in expression.args["ifs"]:
2415            statements.append(f"WHEN {self.sql(e, 'this')}")
2416            statements.append(f"THEN {self.sql(e, 'true')}")
2417
2418        default = self.sql(expression, "default")
2419
2420        if default:
2421            statements.append(f"ELSE {default}")
2422
2423        statements.append("END")
2424
2425        if self.pretty and self.text_width(statements) > self.max_text_width:
2426            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2427
2428        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
2430    def constraint_sql(self, expression: exp.Constraint) -> str:
2431        this = self.sql(expression, "this")
2432        expressions = self.expressions(expression, flat=True)
2433        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
2435    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2436        order = expression.args.get("order")
2437        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2438        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
2440    def extract_sql(self, expression: exp.Extract) -> str:
2441        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2442        expression_sql = self.sql(expression, "expression")
2443        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
2445    def trim_sql(self, expression: exp.Trim) -> str:
2446        trim_type = self.sql(expression, "position")
2447
2448        if trim_type == "LEADING":
2449            return self.func("LTRIM", expression.this)
2450        elif trim_type == "TRAILING":
2451            return self.func("RTRIM", expression.this)
2452        else:
2453            return self.func("TRIM", expression.this, expression.expression)
def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2455    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2456        args = expression.expressions
2457        if isinstance(expression, exp.ConcatWs):
2458            args = args[1:]  # Skip the delimiter
2459
2460        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2461            args = [exp.cast(e, "text") for e in args]
2462
2463        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2464            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2465
2466        return args
def concat_sql(self, expression: sqlglot.expressions.Concat) -> str:
2468    def concat_sql(self, expression: exp.Concat) -> str:
2469        expressions = self.convert_concat_args(expression)
2470
2471        # Some dialects don't allow a single-argument CONCAT call
2472        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2473            return self.sql(expressions[0])
2474
2475        return self.func("CONCAT", *expressions)
def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str:
2477    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2478        return self.func(
2479            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2480        )
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
2482    def check_sql(self, expression: exp.Check) -> str:
2483        this = self.sql(expression, key="this")
2484        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2486    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2487        expressions = self.expressions(expression, flat=True)
2488        reference = self.sql(expression, "reference")
2489        reference = f" {reference}" if reference else ""
2490        delete = self.sql(expression, "delete")
2491        delete = f" ON DELETE {delete}" if delete else ""
2492        update = self.sql(expression, "update")
2493        update = f" ON UPDATE {update}" if update else ""
2494        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2496    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2497        expressions = self.expressions(expression, flat=True)
2498        options = self.expressions(expression, key="options", flat=True, sep=" ")
2499        options = f" {options}" if options else ""
2500        return f"PRIMARY KEY ({expressions}){options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
2502    def if_sql(self, expression: exp.If) -> str:
2503        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
2505    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2506        modifier = expression.args.get("modifier")
2507        modifier = f" {modifier}" if modifier else ""
2508        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
2510    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2511        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:
2513    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2514        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2515        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str:
2517    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2518        if isinstance(expression, exp.JSONPathPart):
2519            transform = self.TRANSFORMS.get(expression.__class__)
2520            if not callable(transform):
2521                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2522                return ""
2523
2524            return transform(self, expression)
2525
2526        if isinstance(expression, int):
2527            return str(expression)
2528
2529        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2530            escaped = expression.replace("'", "\\'")
2531            escaped = f"\\'{expression}\\'"
2532        else:
2533            escaped = expression.replace('"', '\\"')
2534            escaped = f'"{escaped}"'
2535
2536        return escaped
def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str:
2538    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2539        return f"{self.sql(expression, 'this')} FORMAT JSON"
def jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2541    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2542        null_handling = expression.args.get("null_handling")
2543        null_handling = f" {null_handling}" if null_handling else ""
2544
2545        unique_keys = expression.args.get("unique_keys")
2546        if unique_keys is not None:
2547            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2548        else:
2549            unique_keys = ""
2550
2551        return_type = self.sql(expression, "return_type")
2552        return_type = f" RETURNING {return_type}" if return_type else ""
2553        encoding = self.sql(expression, "encoding")
2554        encoding = f" ENCODING {encoding}" if encoding else ""
2555
2556        return self.func(
2557            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2558            *expression.expressions,
2559            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2560        )
def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str:
2562    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2563        return self.jsonobject_sql(expression)
def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str:
2565    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2566        null_handling = expression.args.get("null_handling")
2567        null_handling = f" {null_handling}" if null_handling else ""
2568        return_type = self.sql(expression, "return_type")
2569        return_type = f" RETURNING {return_type}" if return_type else ""
2570        strict = " STRICT" if expression.args.get("strict") else ""
2571        return self.func(
2572            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2573        )
def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str:
2575    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2576        this = self.sql(expression, "this")
2577        order = self.sql(expression, "order")
2578        null_handling = expression.args.get("null_handling")
2579        null_handling = f" {null_handling}" if null_handling else ""
2580        return_type = self.sql(expression, "return_type")
2581        return_type = f" RETURNING {return_type}" if return_type else ""
2582        strict = " STRICT" if expression.args.get("strict") else ""
2583        return self.func(
2584            "JSON_ARRAYAGG",
2585            this,
2586            suffix=f"{order}{null_handling}{return_type}{strict})",
2587        )
def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str:
2589    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2590        path = self.sql(expression, "path")
2591        path = f" PATH {path}" if path else ""
2592        nested_schema = self.sql(expression, "nested_schema")
2593
2594        if nested_schema:
2595            return f"NESTED{path} {nested_schema}"
2596
2597        this = self.sql(expression, "this")
2598        kind = self.sql(expression, "kind")
2599        kind = f" {kind}" if kind else ""
2600        return f"{this}{kind}{path}"
def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str:
2602    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2603        return self.func("COLUMNS", *expression.expressions)
def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str:
2605    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2606        this = self.sql(expression, "this")
2607        path = self.sql(expression, "path")
2608        path = f", {path}" if path else ""
2609        error_handling = expression.args.get("error_handling")
2610        error_handling = f" {error_handling}" if error_handling else ""
2611        empty_handling = expression.args.get("empty_handling")
2612        empty_handling = f" {empty_handling}" if empty_handling else ""
2613        schema = self.sql(expression, "schema")
2614        return self.func(
2615            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2616        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
2618    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2619        this = self.sql(expression, "this")
2620        kind = self.sql(expression, "kind")
2621        path = self.sql(expression, "path")
2622        path = f" {path}" if path else ""
2623        as_json = " AS JSON" if expression.args.get("as_json") else ""
2624        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
2626    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2627        this = self.sql(expression, "this")
2628        path = self.sql(expression, "path")
2629        path = f", {path}" if path else ""
2630        expressions = self.expressions(expression)
2631        with_ = (
2632            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2633            if expressions
2634            else ""
2635        )
2636        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
2638    def in_sql(self, expression: exp.In) -> str:
2639        query = expression.args.get("query")
2640        unnest = expression.args.get("unnest")
2641        field = expression.args.get("field")
2642        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2643
2644        if query:
2645            in_sql = self.wrap(self.sql(query))
2646        elif unnest:
2647            in_sql = self.in_unnest_op(unnest)
2648        elif field:
2649            in_sql = self.sql(field)
2650        else:
2651            in_sql = f"({self.expressions(expression, flat=True)})"
2652
2653        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
2655    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2656        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
2658    def interval_sql(self, expression: exp.Interval) -> str:
2659        unit = self.sql(expression, "unit")
2660        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2661            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2662        unit = f" {unit}" if unit else ""
2663
2664        if self.SINGLE_STRING_INTERVAL:
2665            this = expression.this.name if expression.this else ""
2666            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2667
2668        this = self.sql(expression, "this")
2669        if this:
2670            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2671            this = f" {this}" if unwrapped else f" ({this})"
2672
2673        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
2675    def return_sql(self, expression: exp.Return) -> str:
2676        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
2678    def reference_sql(self, expression: exp.Reference) -> str:
2679        this = self.sql(expression, "this")
2680        expressions = self.expressions(expression, flat=True)
2681        expressions = f"({expressions})" if expressions else ""
2682        options = self.expressions(expression, key="options", flat=True, sep=" ")
2683        options = f" {options}" if options else ""
2684        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
2686    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2687        return self.func(self.sql(expression, "this"), *expression.expressions)
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
2689    def paren_sql(self, expression: exp.Paren) -> str:
2690        if isinstance(expression.unnest(), exp.Select):
2691            sql = self.wrap(expression)
2692        else:
2693            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2694            sql = f"({sql}{self.seg(')', sep='')}"
2695
2696        return self.prepend_ctes(expression, sql)
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
2698    def neg_sql(self, expression: exp.Neg) -> str:
2699        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2700        this_sql = self.sql(expression, "this")
2701        sep = " " if this_sql[0] == "-" else ""
2702        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
2704    def not_sql(self, expression: exp.Not) -> str:
2705        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
2707    def alias_sql(self, expression: exp.Alias) -> str:
2708        alias = self.sql(expression, "alias")
2709        alias = f" AS {alias}" if alias else ""
2710        return f"{self.sql(expression, 'this')}{alias}"
def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str:
2712    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2713        alias = expression.args["alias"]
2714        identifier_alias = isinstance(alias, exp.Identifier)
2715
2716        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2717            alias.replace(exp.Literal.string(alias.output_name))
2718        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2719            alias.replace(exp.to_identifier(alias.output_name))
2720
2721        return self.alias_sql(expression)
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
2723    def aliases_sql(self, expression: exp.Aliases) -> str:
2724        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def atindex_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2726    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2727        this = self.sql(expression, "this")
2728        index = self.sql(expression, "expression")
2729        return f"{this} AT {index}"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2731    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2732        this = self.sql(expression, "this")
2733        zone = self.sql(expression, "zone")
2734        return f"{this} AT TIME ZONE {zone}"
def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str:
2736    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2737        this = self.sql(expression, "this")
2738        zone = self.sql(expression, "zone")
2739        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
2741    def add_sql(self, expression: exp.Add) -> str:
2742        return self.binary(expression, "+")
def and_sql(self, expression: sqlglot.expressions.And) -> str:
2744    def and_sql(self, expression: exp.And) -> str:
2745        return self.connector_sql(expression, "AND")
def xor_sql(self, expression: sqlglot.expressions.Xor) -> str:
2747    def xor_sql(self, expression: exp.Xor) -> str:
2748        return self.connector_sql(expression, "XOR")
def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str:
2750    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2751        if not self.pretty:
2752            return self.binary(expression, op)
2753
2754        sqls = tuple(
2755            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2756            for i, e in enumerate(expression.flatten(unnest=False))
2757        )
2758
2759        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2760        return f"{sep}{op} ".join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
2762    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2763        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
2765    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2766        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
2768    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2769        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
2771    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2772        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
2774    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2775        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
2777    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2778        return self.binary(expression, "^")
def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2780    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2781        format_sql = self.sql(expression, "format")
2782        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2783        to_sql = self.sql(expression, "to")
2784        to_sql = f" {to_sql}" if to_sql else ""
2785        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
2787    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2788        zone = self.sql(expression, "this")
2789        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def currenttimestamp_sql(self, expression: sqlglot.expressions.CurrentTimestamp) -> str:
2791    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2792        return self.func("CURRENT_TIMESTAMP", expression.this)
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
2794    def collate_sql(self, expression: exp.Collate) -> str:
2795        if self.COLLATE_IS_FUNC:
2796            return self.function_fallback_sql(expression)
2797        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
2799    def command_sql(self, expression: exp.Command) -> str:
2800        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
2802    def comment_sql(self, expression: exp.Comment) -> str:
2803        this = self.sql(expression, "this")
2804        kind = expression.args["kind"]
2805        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2806        expression_sql = self.sql(expression, "expression")
2807        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
2809    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2810        this = self.sql(expression, "this")
2811        delete = " DELETE" if expression.args.get("delete") else ""
2812        recompress = self.sql(expression, "recompress")
2813        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2814        to_disk = self.sql(expression, "to_disk")
2815        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2816        to_volume = self.sql(expression, "to_volume")
2817        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2818        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
2820    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2821        where = self.sql(expression, "where")
2822        group = self.sql(expression, "group")
2823        aggregates = self.expressions(expression, key="aggregates")
2824        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2825
2826        if not (where or group or aggregates) and len(expression.expressions) == 1:
2827            return f"TTL {self.expressions(expression, flat=True)}"
2828
2829        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
2831    def transaction_sql(self, expression: exp.Transaction) -> str:
2832        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
2834    def commit_sql(self, expression: exp.Commit) -> str:
2835        chain = expression.args.get("chain")
2836        if chain is not None:
2837            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2838
2839        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
2841    def rollback_sql(self, expression: exp.Rollback) -> str:
2842        savepoint = expression.args.get("savepoint")
2843        savepoint = f" TO {savepoint}" if savepoint else ""
2844        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
2846    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2847        this = self.sql(expression, "this")
2848
2849        dtype = self.sql(expression, "dtype")
2850        if dtype:
2851            collate = self.sql(expression, "collate")
2852            collate = f" COLLATE {collate}" if collate else ""
2853            using = self.sql(expression, "using")
2854            using = f" USING {using}" if using else ""
2855            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2856
2857        default = self.sql(expression, "default")
2858        if default:
2859            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2860
2861        comment = self.sql(expression, "comment")
2862        if comment:
2863            return f"ALTER COLUMN {this} COMMENT {comment}"
2864
2865        if not expression.args.get("drop"):
2866            self.unsupported("Unsupported ALTER COLUMN syntax")
2867
2868        return f"ALTER COLUMN {this} DROP DEFAULT"
def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str:
2870    def renametable_sql(self, expression: exp.RenameTable) -> str:
2871        if not self.RENAME_TABLE_WITH_DB:
2872            # Remove db from tables
2873            expression = expression.transform(
2874                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2875            )
2876        this = self.sql(expression, "this")
2877        return f"RENAME TO {this}"
def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str:
2879    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2880        exists = " IF EXISTS" if expression.args.get("exists") else ""
2881        old_column = self.sql(expression, "this")
2882        new_column = self.sql(expression, "to")
2883        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2885    def altertable_sql(self, expression: exp.AlterTable) -> str:
2886        actions = expression.args["actions"]
2887
2888        if isinstance(actions[0], exp.ColumnDef):
2889            actions = self.add_column_sql(expression)
2890        elif isinstance(actions[0], exp.Schema):
2891            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2892        elif isinstance(actions[0], exp.Delete):
2893            actions = self.expressions(expression, key="actions", flat=True)
2894        else:
2895            actions = self.expressions(expression, key="actions", flat=True)
2896
2897        exists = " IF EXISTS" if expression.args.get("exists") else ""
2898        only = " ONLY" if expression.args.get("only") else ""
2899        options = self.expressions(expression, key="options")
2900        options = f", {options}" if options else ""
2901        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
def add_column_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2903    def add_column_sql(self, expression: exp.AlterTable) -> str:
2904        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2905            return self.expressions(
2906                expression,
2907                key="actions",
2908                prefix="ADD COLUMN ",
2909            )
2910        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
2912    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2913        expressions = self.expressions(expression)
2914        exists = " IF EXISTS " if expression.args.get("exists") else " "
2915        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
2917    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2918        return f"ADD {self.expressions(expression)}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
2920    def distinct_sql(self, expression: exp.Distinct) -> str:
2921        this = self.expressions(expression, flat=True)
2922
2923        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2924            case = exp.case()
2925            for arg in expression.expressions:
2926                case = case.when(arg.is_(exp.null()), exp.null())
2927            this = self.sql(case.else_(f"({this})"))
2928
2929        this = f" {this}" if this else ""
2930
2931        on = self.sql(expression, "on")
2932        on = f" ON {on}" if on else ""
2933        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
2935    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2936        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
2938    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2939        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str:
2941    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2942        this_sql = self.sql(expression, "this")
2943        expression_sql = self.sql(expression, "expression")
2944        kind = "MAX" if expression.args.get("max") else "MIN"
2945        return f"{this_sql} HAVING {kind} {expression_sql}"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
2971    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2972        return self.sql(
2973            exp.Cast(
2974                this=exp.Div(this=expression.this, expression=expression.expression),
2975                to=exp.DataType(this=exp.DataType.Type.INT),
2976            )
2977        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
2979    def dpipe_sql(self, expression: exp.DPipe) -> str:
2980        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2981            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2982        return self.binary(expression, "||")
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
2984    def div_sql(self, expression: exp.Div) -> str:
2985        l, r = expression.left, expression.right
2986
2987        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2988            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2989
2990        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2991            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
2992                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2993
2994        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2995            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2996                return self.sql(
2997                    exp.cast(
2998                        l / r,
2999                        to=exp.DataType.Type.BIGINT,
3000                    )
3001                )
3002
3003        return self.binary(expression, "/")
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
3005    def overlaps_sql(self, expression: exp.Overlaps) -> str:
3006        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
3008    def distance_sql(self, expression: exp.Distance) -> str:
3009        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
3011    def dot_sql(self, expression: exp.Dot) -> str:
3012        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
3014    def eq_sql(self, expression: exp.EQ) -> str:
3015        return self.binary(expression, "=")
def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str:
3017    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
3018        return self.binary(expression, ":=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
3020    def escape_sql(self, expression: exp.Escape) -> str:
3021        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
3023    def glob_sql(self, expression: exp.Glob) -> str:
3024        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
3026    def gt_sql(self, expression: exp.GT) -> str:
3027        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
3029    def gte_sql(self, expression: exp.GTE) -> str:
3030        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
3032    def ilike_sql(self, expression: exp.ILike) -> str:
3033        return self.binary(expression, "ILIKE")
def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str:
3035    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
3036        return self.binary(expression, "ILIKE ANY")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
3038    def is_sql(self, expression: exp.Is) -> str:
3039        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
3040            return self.sql(
3041                expression.this if expression.expression.this else exp.not_(expression.this)
3042            )
3043        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
3045    def like_sql(self, expression: exp.Like) -> str:
3046        return self.binary(expression, "LIKE")
def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str:
3048    def likeany_sql(self, expression: exp.LikeAny) -> str:
3049        return self.binary(expression, "LIKE ANY")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
3051    def similarto_sql(self, expression: exp.SimilarTo) -> str:
3052        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
3054    def lt_sql(self, expression: exp.LT) -> str:
3055        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
3057    def lte_sql(self, expression: exp.LTE) -> str:
3058        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
3060    def mod_sql(self, expression: exp.Mod) -> str:
3061        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
3063    def mul_sql(self, expression: exp.Mul) -> str:
3064        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
3066    def neq_sql(self, expression: exp.NEQ) -> str:
3067        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
3069    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3070        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
3072    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3073        return self.binary(expression, "IS DISTINCT FROM")
def or_sql(self, expression: sqlglot.expressions.Or) -> str:
3075    def or_sql(self, expression: exp.Or) -> str:
3076        return self.connector_sql(expression, "OR")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
3078    def slice_sql(self, expression: exp.Slice) -> str:
3079        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
3081    def sub_sql(self, expression: exp.Sub) -> str:
3082        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
3084    def trycast_sql(self, expression: exp.TryCast) -> str:
3085        return self.cast_sql(expression, safe_prefix="TRY_")
def log_sql(self, expression: sqlglot.expressions.Log) -> str:
3087    def log_sql(self, expression: exp.Log) -> str:
3088        this = expression.this
3089        expr = expression.expression
3090
3091        if not self.dialect.LOG_BASE_FIRST:
3092            this, expr = expr, this
3093
3094        return self.func("LOG", this, expr)
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
3096    def use_sql(self, expression: exp.Use) -> str:
3097        kind = self.sql(expression, "kind")
3098        kind = f" {kind}" if kind else ""
3099        this = self.sql(expression, "this")
3100        this = f" {this}" if this else ""
3101        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
3103    def binary(self, expression: exp.Binary, op: str) -> str:
3104        op = self.maybe_comment(op, comments=expression.comments)
3105        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
3107    def function_fallback_sql(self, expression: exp.Func) -> str:
3108        args = []
3109
3110        for key in expression.arg_types:
3111            arg_value = expression.args.get(key)
3112
3113            if isinstance(arg_value, list):
3114                for value in arg_value:
3115                    args.append(value)
3116            elif arg_value is not None:
3117                args.append(arg_value)
3118
3119        if self.normalize_functions:
3120            name = expression.sql_name()
3121        else:
3122            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3123
3124        return self.func(name, *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3126    def func(
3127        self,
3128        name: str,
3129        *args: t.Optional[exp.Expression | str],
3130        prefix: str = "(",
3131        suffix: str = ")",
3132    ) -> str:
3133        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
3135    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3136        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3137        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3138            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3139        return ", ".join(arg_sqls)
def text_width(self, args: Iterable) -> int:
3141    def text_width(self, args: t.Iterable) -> int:
3142        return sum(len(arg) for arg in args)
def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]:
3144    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3145        return format_time(
3146            self.sql(expression, "format"),
3147            self.dialect.INVERSE_TIME_MAPPING,
3148            self.dialect.INVERSE_TIME_TRIE,
3149        )
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, sep: str = ', ', prefix: str = '') -> str:
3151    def expressions(
3152        self,
3153        expression: t.Optional[exp.Expression] = None,
3154        key: t.Optional[str] = None,
3155        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3156        flat: bool = False,
3157        indent: bool = True,
3158        skip_first: bool = False,
3159        sep: str = ", ",
3160        prefix: str = "",
3161    ) -> str:
3162        expressions = expression.args.get(key or "expressions") if expression else sqls
3163
3164        if not expressions:
3165            return ""
3166
3167        if flat:
3168            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3169
3170        num_sqls = len(expressions)
3171
3172        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3173        pad = " " * self.pad
3174        stripped_sep = sep.strip()
3175
3176        result_sqls = []
3177        for i, e in enumerate(expressions):
3178            sql = self.sql(e, comment=False)
3179            if not sql:
3180                continue
3181
3182            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3183
3184            if self.pretty:
3185                if self.leading_comma:
3186                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3187                else:
3188                    result_sqls.append(
3189                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3190                    )
3191            else:
3192                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3193
3194        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3195        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3197    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3198        flat = flat or isinstance(expression.parent, exp.Properties)
3199        expressions_sql = self.expressions(expression, flat=flat)
3200        if flat:
3201            return f"{op} {expressions_sql}"
3202        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
3204    def naked_property(self, expression: exp.Property) -> str:
3205        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3206        if not property_name:
3207            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3208        return f"{property_name} {self.sql(expression, 'this')}"
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
3210    def tag_sql(self, expression: exp.Tag) -> str:
3211        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
3213    def token_sql(self, token_type: TokenType) -> str:
3214        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
3216    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3217        this = self.sql(expression, "this")
3218        expressions = self.no_identify(self.expressions, expression)
3219        expressions = (
3220            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3221        )
3222        return f"{this}{expressions}"
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
3224    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3225        this = self.sql(expression, "this")
3226        expressions = self.expressions(expression, flat=True)
3227        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
3229    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3230        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
3232    def when_sql(self, expression: exp.When) -> str:
3233        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3234        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3235        condition = self.sql(expression, "condition")
3236        condition = f" AND {condition}" if condition else ""
3237
3238        then_expression = expression.args.get("then")
3239        if isinstance(then_expression, exp.Insert):
3240            then = f"INSERT {self.sql(then_expression, 'this')}"
3241            if "expression" in then_expression.args:
3242                then += f" VALUES {self.sql(then_expression, 'expression')}"
3243        elif isinstance(then_expression, exp.Update):
3244            if isinstance(then_expression.args.get("expressions"), exp.Star):
3245                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3246            else:
3247                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3248        else:
3249            then = self.sql(then_expression)
3250        return f"WHEN {matched}{source}{condition} THEN {then}"
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
3252    def merge_sql(self, expression: exp.Merge) -> str:
3253        table = expression.this
3254        table_alias = ""
3255
3256        hints = table.args.get("hints")
3257        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3258            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3259            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3260
3261        this = self.sql(table)
3262        using = f"USING {self.sql(expression, 'using')}"
3263        on = f"ON {self.sql(expression, 'on')}"
3264        expressions = self.expressions(expression, sep=" ")
3265
3266        return self.prepend_ctes(
3267            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3268        )
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
3270    def tochar_sql(self, expression: exp.ToChar) -> str:
3271        if expression.args.get("format"):
3272            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3273
3274        return self.sql(exp.cast(expression.this, "text"))
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
3276    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3277        this = self.sql(expression, "this")
3278        kind = self.sql(expression, "kind")
3279        settings_sql = self.expressions(expression, key="settings", sep=" ")
3280        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3281        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
3283    def dictrange_sql(self, expression: exp.DictRange) -> str:
3284        this = self.sql(expression, "this")
3285        max = self.sql(expression, "max")
3286        min = self.sql(expression, "min")
3287        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
3289    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3290        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
3292    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3293        return ""
def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str:
3295    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3296        expressions = self.expressions(expression, key="expressions", flat=True)
3297        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3298        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3299        buckets = self.sql(expression, "buckets")
3300        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str:
3302    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3303        this = self.sql(expression, "this")
3304        having = self.sql(expression, "having")
3305
3306        if having:
3307            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3308
3309        return self.func("ANY_VALUE", this)
def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str:
3311    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3312        transform = self.func("TRANSFORM", *expression.expressions)
3313        row_format_before = self.sql(expression, "row_format_before")
3314        row_format_before = f" {row_format_before}" if row_format_before else ""
3315        record_writer = self.sql(expression, "record_writer")
3316        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3317        using = f" USING {self.sql(expression, 'command_script')}"
3318        schema = self.sql(expression, "schema")
3319        schema = f" AS {schema}" if schema else ""
3320        row_format_after = self.sql(expression, "row_format_after")
3321        row_format_after = f" {row_format_after}" if row_format_after else ""
3322        record_reader = self.sql(expression, "record_reader")
3323        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3324        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str:
3326    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3327        key_block_size = self.sql(expression, "key_block_size")
3328        if key_block_size:
3329            return f"KEY_BLOCK_SIZE = {key_block_size}"
3330
3331        using = self.sql(expression, "using")
3332        if using:
3333            return f"USING {using}"
3334
3335        parser = self.sql(expression, "parser")
3336        if parser:
3337            return f"WITH PARSER {parser}"
3338
3339        comment = self.sql(expression, "comment")
3340        if comment:
3341            return f"COMMENT {comment}"
3342
3343        visible = expression.args.get("visible")
3344        if visible is not None:
3345            return "VISIBLE" if visible else "INVISIBLE"
3346
3347        engine_attr = self.sql(expression, "engine_attr")
3348        if engine_attr:
3349            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3350
3351        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3352        if secondary_engine_attr:
3353            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3354
3355        self.unsupported("Unsupported index constraint option.")
3356        return ""
def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str:
3358    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3359        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3360        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str:
3362    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3363        kind = self.sql(expression, "kind")
3364        kind = f"{kind} INDEX" if kind else "INDEX"
3365        this = self.sql(expression, "this")
3366        this = f" {this}" if this else ""
3367        index_type = self.sql(expression, "index_type")
3368        index_type = f" USING {index_type}" if index_type else ""
3369        schema = self.sql(expression, "schema")
3370        schema = f" {schema}" if schema else ""
3371        options = self.expressions(expression, key="options", sep=" ")
3372        options = f" {options}" if options else ""
3373        return f"{kind}{this}{index_type}{schema}{options}"
def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str:
3375    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3376        if self.NVL2_SUPPORTED:
3377            return self.function_fallback_sql(expression)
3378
3379        case = exp.Case().when(
3380            expression.this.is_(exp.null()).not_(copy=False),
3381            expression.args["true"],
3382            copy=False,
3383        )
3384        else_cond = expression.args.get("false")
3385        if else_cond:
3386            case.else_(else_cond, copy=False)
3387
3388        return self.sql(case)
def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str:
3390    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3391        this = self.sql(expression, "this")
3392        expr = self.sql(expression, "expression")
3393        iterator = self.sql(expression, "iterator")
3394        condition = self.sql(expression, "condition")
3395        condition = f" IF {condition}" if condition else ""
3396        return f"{this} FOR {expr} IN {iterator}{condition}"
def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str:
3398    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3399        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str:
3401    def opclass_sql(self, expression: exp.Opclass) -> str:
3402        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def predict_sql(self, expression: sqlglot.expressions.Predict) -> str:
3404    def predict_sql(self, expression: exp.Predict) -> str:
3405        model = self.sql(expression, "this")
3406        model = f"MODEL {model}"
3407        table = self.sql(expression, "expression")
3408        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3409        parameters = self.sql(expression, "params_struct")
3410        return self.func("PREDICT", model, table, parameters or None)
def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str:
3412    def forin_sql(self, expression: exp.ForIn) -> str:
3413        this = self.sql(expression, "this")
3414        expression_sql = self.sql(expression, "expression")
3415        return f"FOR {this} DO {expression_sql}"
def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str:
3417    def refresh_sql(self, expression: exp.Refresh) -> str:
3418        this = self.sql(expression, "this")
3419        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3420        return f"REFRESH {table}{this}"
def operator_sql(self, expression: sqlglot.expressions.Operator) -> str:
3422    def operator_sql(self, expression: exp.Operator) -> str:
3423        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str:
3425    def toarray_sql(self, expression: exp.ToArray) -> str:
3426        arg = expression.this
3427        if not arg.type:
3428            from sqlglot.optimizer.annotate_types import annotate_types
3429
3430            arg = annotate_types(arg)
3431
3432        if arg.is_type(exp.DataType.Type.ARRAY):
3433            return self.sql(arg)
3434
3435        cond_for_null = arg.is_(exp.null())
3436        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:
3438    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3439        this = expression.this
3440        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3441            return self.sql(this)
3442
3443        return self.sql(exp.cast(this, "time"))
def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str:
3445    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3446        this = expression.this
3447        time_format = self.format_time(expression)
3448
3449        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3450            return self.sql(
3451                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3452            )
3453
3454        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3455            return self.sql(this)
3456
3457        return self.sql(exp.cast(this, "date"))
def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str:
3459    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3460        return self.sql(
3461            exp.func(
3462                "DATEDIFF",
3463                expression.this,
3464                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3465                "day",
3466            )
3467        )
def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str:
3469    def lastday_sql(self, expression: exp.LastDay) -> str:
3470        if self.LAST_DAY_SUPPORTS_DATE_PART:
3471            return self.function_fallback_sql(expression)
3472
3473        unit = expression.text("unit")
3474        if unit and unit != "MONTH":
3475            self.unsupported("Date parts are not supported in LAST_DAY.")
3476
3477        return self.func("LAST_DAY", expression.this)
def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str:
3479    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3480        if self.CAN_IMPLEMENT_ARRAY_ANY:
3481            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3482            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3483            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3484            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3485
3486        from sqlglot.dialects import Dialect
3487
3488        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3489        if self.dialect.__class__ != Dialect:
3490            self.unsupported("ARRAY_ANY is unsupported")
3491
3492        return self.function_fallback_sql(expression)
def generateseries_sql(self, expression: sqlglot.expressions.GenerateSeries) -> str:
3518    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
3519        expression.set("is_end_exclusive", None)
3520        return self.function_fallback_sql(expression)
def struct_sql(self, expression: sqlglot.expressions.Struct) -> str:
3522    def struct_sql(self, expression: exp.Struct) -> str:
3523        expression.set(
3524            "expressions",
3525            [
3526                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
3527                for e in expression.expressions
3528            ],
3529        )
3530
3531        return self.function_fallback_sql(expression)
def partitionrange_sql(self, expression: sqlglot.expressions.PartitionRange) -> str:
3533    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
3534        low = self.sql(expression, "this")
3535        high = self.sql(expression, "expression")
3536
3537        return f"{low} TO {high}"
def truncatetable_sql(self, expression: sqlglot.expressions.TruncateTable) -> str:
3539    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
3540        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
3541        tables = f" {self.expressions(expression)}"
3542
3543        exists = " IF EXISTS" if expression.args.get("exists") else ""
3544
3545        on_cluster = self.sql(expression, "cluster")
3546        on_cluster = f" {on_cluster}" if on_cluster else ""
3547
3548        identity = self.sql(expression, "identity")
3549        identity = f" {identity} IDENTITY" if identity else ""
3550
3551        option = self.sql(expression, "option")
3552        option = f" {option}" if option else ""
3553
3554        partition = self.sql(expression, "partition")
3555        partition = f" {partition}" if partition else ""
3556
3557        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
def convert_sql(self, expression: sqlglot.expressions.Convert) -> str:
3561    def convert_sql(self, expression: exp.Convert) -> str:
3562        to = expression.this
3563        value = expression.expression
3564        style = expression.args.get("style")
3565        safe = expression.args.get("safe")
3566        strict = expression.args.get("strict")
3567
3568        if not to or not value:
3569            return ""
3570
3571        # Retrieve length of datatype and override to default if not specified
3572        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
3573            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
3574
3575        transformed: t.Optional[exp.Expression] = None
3576        cast = exp.Cast if strict else exp.TryCast
3577
3578        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
3579        if isinstance(style, exp.Literal) and style.is_int:
3580            from sqlglot.dialects.tsql import TSQL
3581
3582            style_value = style.name
3583            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
3584            if not converted_style:
3585                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
3586
3587            fmt = exp.Literal.string(converted_style)
3588
3589            if to.this == exp.DataType.Type.DATE:
3590                transformed = exp.StrToDate(this=value, format=fmt)
3591            elif to.this == exp.DataType.Type.DATETIME:
3592                transformed = exp.StrToTime(this=value, format=fmt)
3593            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
3594                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
3595            elif to.this == exp.DataType.Type.TEXT:
3596                transformed = exp.TimeToStr(this=value, format=fmt)
3597
3598        if not transformed:
3599            transformed = cast(this=value, to=to, safe=safe)
3600
3601        return self.sql(transformed)