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

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)
428    def __init__(
429        self,
430        pretty: t.Optional[bool] = None,
431        identify: str | bool = False,
432        normalize: bool = False,
433        pad: int = 2,
434        indent: int = 2,
435        normalize_functions: t.Optional[str | bool] = None,
436        unsupported_level: ErrorLevel = ErrorLevel.WARN,
437        max_unsupported: int = 3,
438        leading_comma: bool = False,
439        max_text_width: int = 80,
440        comments: bool = True,
441        dialect: DialectType = None,
442    ):
443        import sqlglot
444        from sqlglot.dialects import Dialect
445
446        self.pretty = pretty if pretty is not None else sqlglot.pretty
447        self.identify = identify
448        self.normalize = normalize
449        self.pad = pad
450        self._indent = indent
451        self.unsupported_level = unsupported_level
452        self.max_unsupported = max_unsupported
453        self.leading_comma = leading_comma
454        self.max_text_width = max_text_width
455        self.comments = comments
456        self.dialect = Dialect.get_or_raise(dialect)
457
458        # This is both a Dialect property and a Generator argument, so we prioritize the latter
459        self.normalize_functions = (
460            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
461        )
462
463        self.unsupported_messages: t.List[str] = []
464        self._escaped_quote_end: str = (
465            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
466        )
467        self._escaped_identifier_end: str = (
468            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
469        )
TRANSFORMS = {<class 'sqlglot.expressions.DateAdd'>: <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.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <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.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.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <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.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.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <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
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
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.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.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:
471    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
472        """
473        Generates the SQL string corresponding to the given syntax tree.
474
475        Args:
476            expression: The syntax tree.
477            copy: Whether or not to copy the expression. The generator performs mutations so
478                it is safer to copy.
479
480        Returns:
481            The SQL string corresponding to `expression`.
482        """
483        if copy:
484            expression = expression.copy()
485
486        expression = self.preprocess(expression)
487
488        self.unsupported_messages = []
489        sql = self.sql(expression).strip()
490
491        if self.pretty:
492            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
493
494        if self.unsupported_level == ErrorLevel.IGNORE:
495            return sql
496
497        if self.unsupported_level == ErrorLevel.WARN:
498            for msg in self.unsupported_messages:
499                logger.warning(msg)
500        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
501            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
502
503        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:
505    def preprocess(self, expression: exp.Expression) -> exp.Expression:
506        """Apply generic preprocessing transformations to a given expression."""
507        if (
508            not expression.parent
509            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
510            and any(node.parent is not expression for node in expression.find_all(exp.With))
511        ):
512            from sqlglot.transforms import move_ctes_to_top_level
513
514            expression = move_ctes_to_top_level(expression)
515
516        if self.ENSURE_BOOLS:
517            from sqlglot.transforms import ensure_bools
518
519            expression = ensure_bools(expression)
520
521        return expression

Apply generic preprocessing transformations to a given expression.

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