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

Apply generic preprocessing transformations to a given expression.

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