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

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

Arguments:
  • pretty: Whether or not 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 or not to normalize identifiers to lowercase. Default: False.
  • pad: Determines the pad size in a formatted string. Default: 2.
  • indent: Determines the indentation size in a formatted string. Default: 2.
  • normalize_functions: Whether or not to normalize all 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: Determines whether or not 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 or not 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)
474    def __init__(
475        self,
476        pretty: t.Optional[bool] = None,
477        identify: str | bool = False,
478        normalize: bool = False,
479        pad: int = 2,
480        indent: int = 2,
481        normalize_functions: t.Optional[str | bool] = None,
482        unsupported_level: ErrorLevel = ErrorLevel.WARN,
483        max_unsupported: int = 3,
484        leading_comma: bool = False,
485        max_text_width: int = 80,
486        comments: bool = True,
487        dialect: DialectType = None,
488    ):
489        import sqlglot
490        from sqlglot.dialects import Dialect
491
492        self.pretty = pretty if pretty is not None else sqlglot.pretty
493        self.identify = identify
494        self.normalize = normalize
495        self.pad = pad
496        self._indent = indent
497        self.unsupported_level = unsupported_level
498        self.max_unsupported = max_unsupported
499        self.leading_comma = leading_comma
500        self.max_text_width = max_text_width
501        self.comments = comments
502        self.dialect = Dialect.get_or_raise(dialect)
503
504        # This is both a Dialect property and a Generator argument, so we prioritize the latter
505        self.normalize_functions = (
506            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
507        )
508
509        self.unsupported_messages: t.List[str] = []
510        self._escaped_quote_end: str = (
511            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
512        )
513        self._escaped_identifier_end: str = (
514            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
515        )
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.CheckColumnConstraint'>: <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.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.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
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = 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'}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_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.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'>)
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
KEY_VALUE_DEFINITIONS = (<class 'sqlglot.expressions.Bracket'>, <class 'sqlglot.expressions.EQ'>, <class 'sqlglot.expressions.PropertyEQ'>, <class 'sqlglot.expressions.Slice'>)
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:
517    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
518        """
519        Generates the SQL string corresponding to the given syntax tree.
520
521        Args:
522            expression: The syntax tree.
523            copy: Whether or not to copy the expression. The generator performs mutations so
524                it is safer to copy.
525
526        Returns:
527            The SQL string corresponding to `expression`.
528        """
529        if copy:
530            expression = expression.copy()
531
532        expression = self.preprocess(expression)
533
534        self.unsupported_messages = []
535        sql = self.sql(expression).strip()
536
537        if self.pretty:
538            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
539
540        if self.unsupported_level == ErrorLevel.IGNORE:
541            return sql
542
543        if self.unsupported_level == ErrorLevel.WARN:
544            for msg in self.unsupported_messages:
545                logger.warning(msg)
546        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
547            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
548
549        return sql

Generates the SQL string corresponding to the given syntax tree.

Arguments:
  • expression: The syntax tree.
  • copy: Whether or not 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:
551    def preprocess(self, expression: exp.Expression) -> exp.Expression:
552        """Apply generic preprocessing transformations to a given expression."""
553        if (
554            not expression.parent
555            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
556            and any(node.parent is not expression for node in expression.find_all(exp.With))
557        ):
558            from sqlglot.transforms import move_ctes_to_top_level
559
560            expression = move_ctes_to_top_level(expression)
561
562        if self.ENSURE_BOOLS:
563            from sqlglot.transforms import ensure_bools
564
565            expression = ensure_bools(expression)
566
567        return expression

Apply generic preprocessing transformations to a given expression.

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