Edit on GitHub

sqlglot.generator

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

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)
384    def __init__(
385        self,
386        pretty: t.Optional[bool] = None,
387        identify: str | bool = False,
388        normalize: bool = False,
389        pad: int = 2,
390        indent: int = 2,
391        normalize_functions: t.Optional[str | bool] = None,
392        unsupported_level: ErrorLevel = ErrorLevel.WARN,
393        max_unsupported: int = 3,
394        leading_comma: bool = False,
395        max_text_width: int = 80,
396        comments: bool = True,
397    ):
398        import sqlglot
399
400        self.pretty = pretty if pretty is not None else sqlglot.pretty
401        self.identify = identify
402        self.normalize = normalize
403        self.pad = pad
404        self._indent = indent
405        self.unsupported_level = unsupported_level
406        self.max_unsupported = max_unsupported
407        self.leading_comma = leading_comma
408        self.max_text_width = max_text_width
409        self.comments = comments
410
411        # This is both a Dialect property and a Generator argument, so we prioritize the latter
412        self.normalize_functions = (
413            self.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
414        )
415
416        self.unsupported_messages: t.List[str] = []
417        self._escaped_quote_end: str = self.TOKENIZER_CLASS.STRING_ESCAPES[0] + self.QUOTE_END
418        self._escaped_identifier_end: str = (
419            self.TOKENIZER_CLASS.IDENTIFIER_ESCAPES[0] + self.IDENTIFIER_END
420        )
421        self._cache: t.Optional[t.Dict[int, str]] = None
TRANSFORMS = {<class 'sqlglot.expressions.DateAdd'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TsOrDsAdd'>: <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.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.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.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <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.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>>}
LOG_BASE_FIRST = True
NULL_ORDERING_SUPPORTED = 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
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SIZE_IS_PERCENT = False
LIMIT_FETCH = 'ALL'
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_ADD_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
SUPPORTS_PARAMETERS = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
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.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.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.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <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.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.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.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'>}
RESERVED_KEYWORDS: Set[str] = set()
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
UNESCAPED_SEQUENCE_TABLE = None
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
INVERSE_TIME_MAPPING: Dict[str, str] = {}
INVERSE_TIME_TRIE: Dict = {}
INDEX_OFFSET = 0
UNNEST_COLUMN_ONLY = False
ALIAS_POST_TABLESAMPLE = False
IDENTIFIERS_CAN_START_WITH_DIGIT = False
STRICT_STRING_CONCAT = False
NORMALIZE_FUNCTIONS: bool | str = 'upper'
NULL_ORDERING = 'nulls_are_small'
@classmethod
def can_identify(text: str, identify: str | bool = 'safe') -> bool:
272    @classmethod
273    def can_identify(cls, text: str, identify: str | bool = "safe") -> bool:
274        """Checks if text can be identified given an identify option.
275
276        Args:
277            text: The text to check.
278            identify:
279                "always" or `True`: Always returns true.
280                "safe": True if the identifier is case-insensitive.
281
282        Returns:
283            Whether or not the given text can be identified.
284        """
285        if identify is True or identify == "always":
286            return True
287
288        if identify == "safe":
289            return not cls.case_sensitive(text)
290
291        return False

Checks if text can be identified given an identify option.

Arguments:
  • text: The text to check.
  • identify: "always" or True: Always returns true. "safe": True if the identifier is case-insensitive.
Returns:

Whether or not the given text can be identified.

QUOTE_START = "'"
QUOTE_END = "'"
IDENTIFIER_START = '"'
IDENTIFIER_END = '"'
TOKENIZER_CLASS = <class 'sqlglot.tokens.Tokenizer'>
BIT_START: Optional[str] = None
BIT_END: Optional[str] = None
HEX_START: Optional[str] = None
HEX_END: Optional[str] = None
BYTE_START: Optional[str] = None
BYTE_END: Optional[str] = None
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: Optional[sqlglot.expressions.Expression], cache: Optional[Dict[int, str]] = None) -> str:
423    def generate(
424        self,
425        expression: t.Optional[exp.Expression],
426        cache: t.Optional[t.Dict[int, str]] = None,
427    ) -> str:
428        """
429        Generates the SQL string corresponding to the given syntax tree.
430
431        Args:
432            expression: The syntax tree.
433            cache: An optional sql string cache. This leverages the hash of an Expression
434                which can be slow to compute, so only use it if you set _hash on each node.
435
436        Returns:
437            The SQL string corresponding to `expression`.
438        """
439        if cache is not None:
440            self._cache = cache
441
442        self.unsupported_messages = []
443        sql = self.sql(expression).strip()
444        self._cache = None
445
446        if self.unsupported_level == ErrorLevel.IGNORE:
447            return sql
448
449        if self.unsupported_level == ErrorLevel.WARN:
450            for msg in self.unsupported_messages:
451                logger.warning(msg)
452        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
453            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
454
455        if self.pretty:
456            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
457        return sql

Generates the SQL string corresponding to the given syntax tree.

Arguments:
  • expression: The syntax tree.
  • cache: An optional sql string cache. This leverages the hash of an Expression which can be slow to compute, so only use it if you set _hash on each node.
Returns:

The SQL string corresponding to expression.

def unsupported(self, message: str) -> None:
459    def unsupported(self, message: str) -> None:
460        if self.unsupported_level == ErrorLevel.IMMEDIATE:
461            raise UnsupportedError(message)
462        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
464    def sep(self, sep: str = " ") -> str:
465        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
467    def seg(self, sql: str, sep: str = " ") -> str:
468        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
470    def pad_comment(self, comment: str) -> str:
471        comment = " " + comment if comment[0].strip() else comment
472        comment = comment + " " if comment[-1].strip() else comment
473        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None) -> str:
475    def maybe_comment(
476        self,
477        sql: str,
478        expression: t.Optional[exp.Expression] = None,
479        comments: t.Optional[t.List[str]] = None,
480    ) -> str:
481        comments = (
482            ((expression and expression.comments) if comments is None else comments)  # type: ignore
483            if self.comments
484            else None
485        )
486
487        if not comments or isinstance(expression, exp.Binary):
488            return sql
489
490        sep = "\n" if self.pretty else " "
491        comments_sql = sep.join(
492            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
493        )
494
495        if not comments_sql:
496            return sql
497
498        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
499            return (
500                f"{self.sep()}{comments_sql}{sql}"
501                if sql[0].isspace()
502                else f"{comments_sql}{self.sep()}{sql}"
503            )
504
505        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
507    def wrap(self, expression: exp.Expression | str) -> str:
508        this_sql = self.indent(
509            self.sql(expression)
510            if isinstance(expression, (exp.Select, exp.Union))
511            else self.sql(expression, "this"),
512            level=1,
513            pad=0,
514        )
515        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
517    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
518        original = self.identify
519        self.identify = False
520        result = func(*args, **kwargs)
521        self.identify = original
522        return result
def normalize_func(self, name: str) -> str:
524    def normalize_func(self, name: str) -> str:
525        if self.normalize_functions == "upper" or self.normalize_functions is True:
526            return name.upper()
527        if self.normalize_functions == "lower":
528            return name.lower()
529        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
531    def indent(
532        self,
533        sql: str,
534        level: int = 0,
535        pad: t.Optional[int] = None,
536        skip_first: bool = False,
537        skip_last: bool = False,
538    ) -> str:
539        if not self.pretty:
540            return sql
541
542        pad = self.pad if pad is None else pad
543        lines = sql.split("\n")
544
545        return "\n".join(
546            line
547            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
548            else f"{' ' * (level * self._indent + pad)}{line}"
549            for i, line in enumerate(lines)
550        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
552    def sql(
553        self,
554        expression: t.Optional[str | exp.Expression],
555        key: t.Optional[str] = None,
556        comment: bool = True,
557    ) -> str:
558        if not expression:
559            return ""
560
561        if isinstance(expression, str):
562            return expression
563
564        if key:
565            value = expression.args.get(key)
566            if value:
567                return self.sql(value)
568            return ""
569
570        if self._cache is not None:
571            expression_id = hash(expression)
572
573            if expression_id in self._cache:
574                return self._cache[expression_id]
575
576        transform = self.TRANSFORMS.get(expression.__class__)
577
578        if callable(transform):
579            sql = transform(self, expression)
580        elif transform:
581            sql = transform
582        elif isinstance(expression, exp.Expression):
583            exp_handler_name = f"{expression.key}_sql"
584
585            if hasattr(self, exp_handler_name):
586                sql = getattr(self, exp_handler_name)(expression)
587            elif isinstance(expression, exp.Func):
588                sql = self.function_fallback_sql(expression)
589            elif isinstance(expression, exp.Property):
590                sql = self.property_sql(expression)
591            else:
592                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
593        else:
594            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
595
596        sql = self.maybe_comment(sql, expression) if self.comments and comment else sql
597
598        if self._cache is not None:
599            self._cache[expression_id] = sql
600        return sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
602    def uncache_sql(self, expression: exp.Uncache) -> str:
603        table = self.sql(expression, "this")
604        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
605        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
607    def cache_sql(self, expression: exp.Cache) -> str:
608        lazy = " LAZY" if expression.args.get("lazy") else ""
609        table = self.sql(expression, "this")
610        options = expression.args.get("options")
611        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
612        sql = self.sql(expression, "expression")
613        sql = f" AS{self.sep()}{sql}" if sql else ""
614        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
615        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
617    def characterset_sql(self, expression: exp.CharacterSet) -> str:
618        if isinstance(expression.parent, exp.Cast):
619            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
620        default = "DEFAULT " if expression.args.get("default") else ""
621        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
623    def column_sql(self, expression: exp.Column) -> str:
624        join_mark = " (+)" if expression.args.get("join_mark") else ""
625
626        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
627            join_mark = ""
628            self.unsupported("Outer join syntax using the (+) operator is not supported.")
629
630        column = ".".join(
631            self.sql(part)
632            for part in (
633                expression.args.get("catalog"),
634                expression.args.get("db"),
635                expression.args.get("table"),
636                expression.args.get("this"),
637            )
638            if part
639        )
640
641        return f"{column}{join_mark}"
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
643    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
644        this = self.sql(expression, "this")
645        this = f" {this}" if this else ""
646        position = self.sql(expression, "position")
647        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
649    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
650        column = self.sql(expression, "this")
651        kind = self.sql(expression, "kind")
652        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
653        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
654        kind = f"{sep}{kind}" if kind else ""
655        constraints = f" {constraints}" if constraints else ""
656        position = self.sql(expression, "position")
657        position = f" {position}" if position else ""
658
659        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
660            kind = ""
661
662        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
664    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
665        this = self.sql(expression, "this")
666        kind_sql = self.sql(expression, "kind").strip()
667        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
669    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
670        this = self.sql(expression, "this")
671        if expression.args.get("not_null"):
672            persisted = " PERSISTED NOT NULL"
673        elif expression.args.get("persisted"):
674            persisted = " PERSISTED"
675        else:
676            persisted = ""
677        return f"AS {this}{persisted}"
def autoincrementcolumnconstraint_sql(self, _) -> str:
679    def autoincrementcolumnconstraint_sql(self, _) -> str:
680        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
682    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
683        if isinstance(expression.this, list):
684            this = self.wrap(self.expressions(expression, key="this", flat=True))
685        else:
686            this = self.sql(expression, "this")
687
688        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
690    def generatedasidentitycolumnconstraint_sql(
691        self, expression: exp.GeneratedAsIdentityColumnConstraint
692    ) -> str:
693        this = ""
694        if expression.this is not None:
695            on_null = " ON NULL" if expression.args.get("on_null") else ""
696            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
697
698        start = expression.args.get("start")
699        start = f"START WITH {start}" if start else ""
700        increment = expression.args.get("increment")
701        increment = f" INCREMENT BY {increment}" if increment else ""
702        minvalue = expression.args.get("minvalue")
703        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
704        maxvalue = expression.args.get("maxvalue")
705        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
706        cycle = expression.args.get("cycle")
707        cycle_sql = ""
708
709        if cycle is not None:
710            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
711            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
712
713        sequence_opts = ""
714        if start or increment or cycle_sql:
715            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
716            sequence_opts = f" ({sequence_opts.strip()})"
717
718        expr = self.sql(expression, "expression")
719        expr = f"({expr})" if expr else "IDENTITY"
720
721        return f"GENERATED{this} AS {expr}{sequence_opts}"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
723    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
724        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
726    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
727        desc = expression.args.get("desc")
728        if desc is not None:
729            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
730        return f"PRIMARY KEY"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
732    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
733        this = self.sql(expression, "this")
734        this = f" {this}" if this else ""
735        index_type = expression.args.get("index_type")
736        index_type = f" USING {index_type}" if index_type else ""
737        return f"UNIQUE{this}{index_type}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str:
739    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
740        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
742    def create_sql(self, expression: exp.Create) -> str:
743        kind = self.sql(expression, "kind").upper()
744        properties = expression.args.get("properties")
745        properties_locs = self.locate_properties(properties) if properties else defaultdict()
746
747        this = self.createable_sql(expression, properties_locs)
748
749        properties_sql = ""
750        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
751            exp.Properties.Location.POST_WITH
752        ):
753            properties_sql = self.sql(
754                exp.Properties(
755                    expressions=[
756                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
757                        *properties_locs[exp.Properties.Location.POST_WITH],
758                    ]
759                )
760            )
761
762        begin = " BEGIN" if expression.args.get("begin") else ""
763        expression_sql = self.sql(expression, "expression")
764        if expression_sql:
765            expression_sql = f"{begin}{self.sep()}{expression_sql}"
766
767            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
768                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
769                    postalias_props_sql = self.properties(
770                        exp.Properties(
771                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
772                        ),
773                        wrapped=False,
774                    )
775                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
776                else:
777                    expression_sql = f" AS{expression_sql}"
778
779        postindex_props_sql = ""
780        if properties_locs.get(exp.Properties.Location.POST_INDEX):
781            postindex_props_sql = self.properties(
782                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
783                wrapped=False,
784                prefix=" ",
785            )
786
787        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
788        indexes = f" {indexes}" if indexes else ""
789        index_sql = indexes + postindex_props_sql
790
791        replace = " OR REPLACE" if expression.args.get("replace") else ""
792        unique = " UNIQUE" if expression.args.get("unique") else ""
793
794        postcreate_props_sql = ""
795        if properties_locs.get(exp.Properties.Location.POST_CREATE):
796            postcreate_props_sql = self.properties(
797                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
798                sep=" ",
799                prefix=" ",
800                wrapped=False,
801            )
802
803        modifiers = "".join((replace, unique, postcreate_props_sql))
804
805        postexpression_props_sql = ""
806        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
807            postexpression_props_sql = self.properties(
808                exp.Properties(
809                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
810                ),
811                sep=" ",
812                prefix=" ",
813                wrapped=False,
814            )
815
816        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
817        no_schema_binding = (
818            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
819        )
820
821        clone = self.sql(expression, "clone")
822        clone = f" {clone}" if clone else ""
823
824        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
825        return self.prepend_ctes(expression, expression_sql)
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
827    def clone_sql(self, expression: exp.Clone) -> str:
828        this = self.sql(expression, "this")
829        shallow = "SHALLOW " if expression.args.get("shallow") else ""
830        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
831        this = f"{shallow}{keyword} {this}"
832        when = self.sql(expression, "when")
833
834        if when:
835            kind = self.sql(expression, "kind")
836            expr = self.sql(expression, "expression")
837            return f"{this} {when} ({kind} => {expr})"
838
839        return this
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
841    def describe_sql(self, expression: exp.Describe) -> str:
842        return f"DESCRIBE {self.sql(expression, 'this')}"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
844    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
845        with_ = self.sql(expression, "with")
846        if with_:
847            sql = f"{with_}{self.sep()}{sql}"
848        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
850    def with_sql(self, expression: exp.With) -> str:
851        sql = self.expressions(expression, flat=True)
852        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
853
854        return f"WITH {recursive}{sql}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
856    def cte_sql(self, expression: exp.CTE) -> str:
857        alias = self.sql(expression, "alias")
858        return f"{alias} AS {self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
860    def tablealias_sql(self, expression: exp.TableAlias) -> str:
861        alias = self.sql(expression, "this")
862        columns = self.expressions(expression, key="columns", flat=True)
863        columns = f"({columns})" if columns else ""
864        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
866    def bitstring_sql(self, expression: exp.BitString) -> str:
867        this = self.sql(expression, "this")
868        if self.BIT_START:
869            return f"{self.BIT_START}{this}{self.BIT_END}"
870        return f"{int(this, 2)}"
def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str:
872    def hexstring_sql(self, expression: exp.HexString) -> str:
873        this = self.sql(expression, "this")
874        if self.HEX_START:
875            return f"{self.HEX_START}{this}{self.HEX_END}"
876        return f"{int(this, 16)}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
878    def bytestring_sql(self, expression: exp.ByteString) -> str:
879        this = self.sql(expression, "this")
880        if self.BYTE_START:
881            return f"{self.BYTE_START}{this}{self.BYTE_END}"
882        return this
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
884    def rawstring_sql(self, expression: exp.RawString) -> str:
885        string = self.escape_str(expression.this.replace("\\", "\\\\"))
886        return f"{self.QUOTE_START}{string}{self.QUOTE_END}"
def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str:
888    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
889        this = self.sql(expression, "this")
890        specifier = self.sql(expression, "expression")
891        specifier = f" {specifier}" if specifier else ""
892        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
894    def datatype_sql(self, expression: exp.DataType) -> str:
895        type_value = expression.this
896
897        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
898            type_sql = self.sql(expression, "kind")
899        else:
900            type_sql = (
901                self.TYPE_MAPPING.get(type_value, type_value.value)
902                if isinstance(type_value, exp.DataType.Type)
903                else type_value
904            )
905
906        nested = ""
907        interior = self.expressions(expression, flat=True)
908        values = ""
909
910        if interior:
911            if expression.args.get("nested"):
912                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
913                if expression.args.get("values") is not None:
914                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
915                    values = self.expressions(expression, key="values", flat=True)
916                    values = f"{delimiters[0]}{values}{delimiters[1]}"
917            elif type_value == exp.DataType.Type.INTERVAL:
918                nested = f" {interior}"
919            else:
920                nested = f"({interior})"
921
922        type_sql = f"{type_sql}{nested}{values}"
923        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
924            exp.DataType.Type.TIMETZ,
925            exp.DataType.Type.TIMESTAMPTZ,
926        ):
927            type_sql = f"{type_sql} WITH TIME ZONE"
928
929        return type_sql
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
931    def directory_sql(self, expression: exp.Directory) -> str:
932        local = "LOCAL " if expression.args.get("local") else ""
933        row_format = self.sql(expression, "row_format")
934        row_format = f" {row_format}" if row_format else ""
935        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
937    def delete_sql(self, expression: exp.Delete) -> str:
938        this = self.sql(expression, "this")
939        this = f" FROM {this}" if this else ""
940        using = self.sql(expression, "using")
941        using = f" USING {using}" if using else ""
942        where = self.sql(expression, "where")
943        returning = self.sql(expression, "returning")
944        limit = self.sql(expression, "limit")
945        tables = self.expressions(expression, key="tables")
946        tables = f" {tables}" if tables else ""
947        if self.RETURNING_END:
948            expression_sql = f"{this}{using}{where}{returning}{limit}"
949        else:
950            expression_sql = f"{returning}{this}{using}{where}{limit}"
951        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
953    def drop_sql(self, expression: exp.Drop) -> str:
954        this = self.sql(expression, "this")
955        kind = expression.args["kind"]
956        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
957        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
958        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
959        cascade = " CASCADE" if expression.args.get("cascade") else ""
960        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
961        purge = " PURGE" if expression.args.get("purge") else ""
962        return (
963            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
964        )
def except_sql(self, expression: sqlglot.expressions.Except) -> str:
966    def except_sql(self, expression: exp.Except) -> str:
967        return self.prepend_ctes(
968            expression,
969            self.set_operation(expression, self.except_op(expression)),
970        )
def except_op(self, expression: sqlglot.expressions.Except) -> str:
972    def except_op(self, expression: exp.Except) -> str:
973        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
975    def fetch_sql(self, expression: exp.Fetch) -> str:
976        direction = expression.args.get("direction")
977        direction = f" {direction.upper()}" if direction else ""
978        count = expression.args.get("count")
979        count = f" {count}" if count else ""
980        if expression.args.get("percent"):
981            count = f"{count} PERCENT"
982        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
983        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
985    def filter_sql(self, expression: exp.Filter) -> str:
986        if self.AGGREGATE_FILTER_SUPPORTED:
987            this = self.sql(expression, "this")
988            where = self.sql(expression, "expression").strip()
989            return f"{this} FILTER({where})"
990
991        agg = expression.this.copy()
992        agg_arg = agg.this
993        cond = expression.expression.this
994        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
995        return self.sql(agg)
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
 997    def hint_sql(self, expression: exp.Hint) -> str:
 998        if not self.QUERY_HINTS:
 999            self.unsupported("Hints are not supported")
1000            return ""
1001
1002        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
1004    def index_sql(self, expression: exp.Index) -> str:
1005        unique = "UNIQUE " if expression.args.get("unique") else ""
1006        primary = "PRIMARY " if expression.args.get("primary") else ""
1007        amp = "AMP " if expression.args.get("amp") else ""
1008        name = self.sql(expression, "this")
1009        name = f"{name} " if name else ""
1010        table = self.sql(expression, "table")
1011        table = f"{self.INDEX_ON} {table}" if table else ""
1012        using = self.sql(expression, "using")
1013        using = f" USING {using}" if using else ""
1014        index = "INDEX " if not table else ""
1015        columns = self.expressions(expression, key="columns", flat=True)
1016        columns = f"({columns})" if columns else ""
1017        partition_by = self.expressions(expression, key="partition_by", flat=True)
1018        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1019        where = self.sql(expression, "where")
1020        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
1022    def identifier_sql(self, expression: exp.Identifier) -> str:
1023        text = expression.name
1024        lower = text.lower()
1025        text = lower if self.normalize and not expression.quoted else text
1026        text = text.replace(self.IDENTIFIER_END, self._escaped_identifier_end)
1027        if (
1028            expression.quoted
1029            or self.can_identify(text, self.identify)
1030            or lower in self.RESERVED_KEYWORDS
1031            or (not self.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1032        ):
1033            text = f"{self.IDENTIFIER_START}{text}{self.IDENTIFIER_END}"
1034        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
1036    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1037        input_format = self.sql(expression, "input_format")
1038        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1039        output_format = self.sql(expression, "output_format")
1040        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1041        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
1043    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1044        string = self.sql(exp.Literal.string(expression.name))
1045        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
1047    def partition_sql(self, expression: exp.Partition) -> str:
1048        return f"PARTITION({self.expressions(expression, flat=True)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
1050    def properties_sql(self, expression: exp.Properties) -> str:
1051        root_properties = []
1052        with_properties = []
1053
1054        for p in expression.expressions:
1055            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1056            if p_loc == exp.Properties.Location.POST_WITH:
1057                with_properties.append(p.copy())
1058            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1059                root_properties.append(p.copy())
1060
1061        return self.root_properties(
1062            exp.Properties(expressions=root_properties)
1063        ) + self.with_properties(exp.Properties(expressions=with_properties))
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
1065    def root_properties(self, properties: exp.Properties) -> str:
1066        if properties.expressions:
1067            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1068        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1070    def properties(
1071        self,
1072        properties: exp.Properties,
1073        prefix: str = "",
1074        sep: str = ", ",
1075        suffix: str = "",
1076        wrapped: bool = True,
1077    ) -> str:
1078        if properties.expressions:
1079            expressions = self.expressions(properties, sep=sep, indent=False)
1080            if expressions:
1081                expressions = self.wrap(expressions) if wrapped else expressions
1082                return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
1083        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
1085    def with_properties(self, properties: exp.Properties) -> str:
1086        return self.properties(properties, prefix=self.seg("WITH"))
def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict:
1088    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1089        properties_locs = defaultdict(list)
1090        for p in properties.expressions:
1091            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1092            if p_loc != exp.Properties.Location.UNSUPPORTED:
1093                properties_locs[p_loc].append(p.copy())
1094            else:
1095                self.unsupported(f"Unsupported property {p.key}")
1096
1097        return properties_locs
def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1099    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1100        if isinstance(expression.this, exp.Dot):
1101            return self.sql(expression, "this")
1102        return f"'{expression.name}'" if string_key else expression.name
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
1104    def property_sql(self, expression: exp.Property) -> str:
1105        property_cls = expression.__class__
1106        if property_cls == exp.Property:
1107            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1108
1109        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1110        if not property_name:
1111            self.unsupported(f"Unsupported property {expression.key}")
1112
1113        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
1115    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1116        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1117        options = f" {options}" if options else ""
1118        return f"LIKE {self.sql(expression, 'this')}{options}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
1120    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1121        no = "NO " if expression.args.get("no") else ""
1122        protection = " PROTECTION" if expression.args.get("protection") else ""
1123        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
1125    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1126        no = "NO " if expression.args.get("no") else ""
1127        local = expression.args.get("local")
1128        local = f"{local} " if local else ""
1129        dual = "DUAL " if expression.args.get("dual") else ""
1130        before = "BEFORE " if expression.args.get("before") else ""
1131        after = "AFTER " if expression.args.get("after") else ""
1132        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1134    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1135        freespace = self.sql(expression, "this")
1136        percent = " PERCENT" if expression.args.get("percent") else ""
1137        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1139    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1140        if expression.args.get("default"):
1141            property = "DEFAULT"
1142        elif expression.args.get("on"):
1143            property = "ON"
1144        else:
1145            property = "OFF"
1146        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1148    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1149        if expression.args.get("no"):
1150            return "NO MERGEBLOCKRATIO"
1151        if expression.args.get("default"):
1152            return "DEFAULT MERGEBLOCKRATIO"
1153
1154        percent = " PERCENT" if expression.args.get("percent") else ""
1155        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1157    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1158        default = expression.args.get("default")
1159        minimum = expression.args.get("minimum")
1160        maximum = expression.args.get("maximum")
1161        if default or minimum or maximum:
1162            if default:
1163                prop = "DEFAULT"
1164            elif minimum:
1165                prop = "MINIMUM"
1166            else:
1167                prop = "MAXIMUM"
1168            return f"{prop} DATABLOCKSIZE"
1169        units = expression.args.get("units")
1170        units = f" {units}" if units else ""
1171        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1173    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1174        autotemp = expression.args.get("autotemp")
1175        always = expression.args.get("always")
1176        default = expression.args.get("default")
1177        manual = expression.args.get("manual")
1178        never = expression.args.get("never")
1179
1180        if autotemp is not None:
1181            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1182        elif always:
1183            prop = "ALWAYS"
1184        elif default:
1185            prop = "DEFAULT"
1186        elif manual:
1187            prop = "MANUAL"
1188        elif never:
1189            prop = "NEVER"
1190        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1192    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1193        no = expression.args.get("no")
1194        no = " NO" if no else ""
1195        concurrent = expression.args.get("concurrent")
1196        concurrent = " CONCURRENT" if concurrent else ""
1197
1198        for_ = ""
1199        if expression.args.get("for_all"):
1200            for_ = " FOR ALL"
1201        elif expression.args.get("for_insert"):
1202            for_ = " FOR INSERT"
1203        elif expression.args.get("for_none"):
1204            for_ = " FOR NONE"
1205        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1207    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1208        kind = expression.args.get("kind")
1209        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1210        for_or_in = expression.args.get("for_or_in")
1211        lock_type = expression.args.get("lock_type")
1212        override = " OVERRIDE" if expression.args.get("override") else ""
1213        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1215    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1216        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1217        statistics = expression.args.get("statistics")
1218        statistics_sql = ""
1219        if statistics is not None:
1220            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1221        return f"{data_sql}{statistics_sql}"
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1223    def insert_sql(self, expression: exp.Insert) -> str:
1224        overwrite = expression.args.get("overwrite")
1225
1226        if isinstance(expression.this, exp.Directory):
1227            this = " OVERWRITE" if overwrite else " INTO"
1228        else:
1229            this = " OVERWRITE TABLE" if overwrite else " INTO"
1230
1231        alternative = expression.args.get("alternative")
1232        alternative = f" OR {alternative}" if alternative else ""
1233        ignore = " IGNORE" if expression.args.get("ignore") else ""
1234
1235        this = f"{this} {self.sql(expression, 'this')}"
1236
1237        exists = " IF EXISTS" if expression.args.get("exists") else ""
1238        partition_sql = (
1239            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1240        )
1241        where = self.sql(expression, "where")
1242        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1243        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1244        conflict = self.sql(expression, "conflict")
1245        by_name = " BY NAME" if expression.args.get("by_name") else ""
1246        returning = self.sql(expression, "returning")
1247
1248        if self.RETURNING_END:
1249            expression_sql = f"{expression_sql}{conflict}{returning}"
1250        else:
1251            expression_sql = f"{returning}{expression_sql}{conflict}"
1252
1253        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1254        return self.prepend_ctes(expression, sql)
def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str:
1256    def intersect_sql(self, expression: exp.Intersect) -> str:
1257        return self.prepend_ctes(
1258            expression,
1259            self.set_operation(expression, self.intersect_op(expression)),
1260        )
def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str:
1262    def intersect_op(self, expression: exp.Intersect) -> str:
1263        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1265    def introducer_sql(self, expression: exp.Introducer) -> str:
1266        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def kill_sql(self, expression: sqlglot.expressions.Kill) -> str:
1268    def kill_sql(self, expression: exp.Kill) -> str:
1269        kind = self.sql(expression, "kind")
1270        kind = f" {kind}" if kind else ""
1271        this = self.sql(expression, "this")
1272        this = f" {this}" if this else ""
1273        return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1275    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1276        return expression.name.upper()
def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str:
1278    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1279        return expression.name.upper()
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1281    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1282        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1283        constraint = self.sql(expression, "constraint")
1284        if constraint:
1285            constraint = f"ON CONSTRAINT {constraint}"
1286        key = self.expressions(expression, key="key", flat=True)
1287        do = "" if expression.args.get("duplicate") else " DO "
1288        nothing = "NOTHING" if expression.args.get("nothing") else ""
1289        expressions = self.expressions(expression, flat=True)
1290        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1291        if expressions:
1292            expressions = f"UPDATE {set_keyword}{expressions}"
1293        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1295    def returning_sql(self, expression: exp.Returning) -> str:
1296        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1298    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1299        fields = expression.args.get("fields")
1300        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1301        escaped = expression.args.get("escaped")
1302        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1303        items = expression.args.get("collection_items")
1304        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1305        keys = expression.args.get("map_keys")
1306        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1307        lines = expression.args.get("lines")
1308        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1309        null = expression.args.get("null")
1310        null = f" NULL DEFINED AS {null}" if null else ""
1311        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str:
1313    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1314        return f"WITH ({self.expressions(expression, flat=True)})"
def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str:
1316    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1317        this = f"{self.sql(expression, 'this')} INDEX"
1318        target = self.sql(expression, "target")
1319        target = f" FOR {target}" if target else ""
1320        return f"{this}{target} ({self.expressions(expression, flat=True)})"
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
1322    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1323        table = ".".join(
1324            part
1325            for part in [
1326                self.sql(expression, "catalog"),
1327                self.sql(expression, "db"),
1328                self.sql(expression, "this"),
1329            ]
1330            if part
1331        )
1332
1333        version = self.sql(expression, "version")
1334        version = f" {version}" if version else ""
1335        alias = self.sql(expression, "alias")
1336        alias = f"{sep}{alias}" if alias else ""
1337        hints = self.expressions(expression, key="hints", sep=" ")
1338        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1339        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1340        pivots = f" {pivots}" if pivots else ""
1341        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1342        laterals = self.expressions(expression, key="laterals", sep="")
1343
1344        return f"{table}{version}{alias}{hints}{pivots}{joins}{laterals}"
def tablesample_sql( self, expression: sqlglot.expressions.TableSample, seed_prefix: str = 'SEED', sep=' AS ') -> str:
1346    def tablesample_sql(
1347        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
1348    ) -> str:
1349        if self.ALIAS_POST_TABLESAMPLE and expression.this.alias:
1350            table = expression.this.copy()
1351            table.set("alias", None)
1352            this = self.sql(table)
1353            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1354        else:
1355            this = self.sql(expression, "this")
1356            alias = ""
1357        method = self.sql(expression, "method")
1358        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1359        numerator = self.sql(expression, "bucket_numerator")
1360        denominator = self.sql(expression, "bucket_denominator")
1361        field = self.sql(expression, "bucket_field")
1362        field = f" ON {field}" if field else ""
1363        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1364        percent = self.sql(expression, "percent")
1365        percent = f"{percent} PERCENT" if percent else ""
1366        rows = self.sql(expression, "rows")
1367        rows = f"{rows} ROWS" if rows else ""
1368        size = self.sql(expression, "size")
1369        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
1370            size = f"{size} PERCENT"
1371        seed = self.sql(expression, "seed")
1372        seed = f" {seed_prefix} ({seed})" if seed else ""
1373        kind = expression.args.get("kind", "TABLESAMPLE")
1374        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
1376    def pivot_sql(self, expression: exp.Pivot) -> str:
1377        expressions = self.expressions(expression, flat=True)
1378
1379        if expression.this:
1380            this = self.sql(expression, "this")
1381            on = f"{self.seg('ON')} {expressions}"
1382            using = self.expressions(expression, key="using", flat=True)
1383            using = f"{self.seg('USING')} {using}" if using else ""
1384            group = self.sql(expression, "group")
1385            return f"PIVOT {this}{on}{using}{group}"
1386
1387        alias = self.sql(expression, "alias")
1388        alias = f" AS {alias}" if alias else ""
1389        unpivot = expression.args.get("unpivot")
1390        direction = "UNPIVOT" if unpivot else "PIVOT"
1391        field = self.sql(expression, "field")
1392        include_nulls = expression.args.get("include_nulls")
1393        if include_nulls is not None:
1394            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1395        else:
1396            nulls = ""
1397        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
def version_sql(self, expression: sqlglot.expressions.Version) -> str:
1399    def version_sql(self, expression: exp.Version) -> str:
1400        this = f"FOR {expression.name}"
1401        kind = expression.text("kind")
1402        expr = self.sql(expression, "expression")
1403        return f"{this} {kind} {expr}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
1405    def tuple_sql(self, expression: exp.Tuple) -> str:
1406        return f"({self.expressions(expression, flat=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
1408    def update_sql(self, expression: exp.Update) -> str:
1409        this = self.sql(expression, "this")
1410        set_sql = self.expressions(expression, flat=True)
1411        from_sql = self.sql(expression, "from")
1412        where_sql = self.sql(expression, "where")
1413        returning = self.sql(expression, "returning")
1414        order = self.sql(expression, "order")
1415        limit = self.sql(expression, "limit")
1416        if self.RETURNING_END:
1417            expression_sql = f"{from_sql}{where_sql}{returning}"
1418        else:
1419            expression_sql = f"{returning}{from_sql}{where_sql}"
1420        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1421        return self.prepend_ctes(expression, sql)
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
1423    def values_sql(self, expression: exp.Values) -> str:
1424        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1425        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1426            args = self.expressions(expression)
1427            alias = self.sql(expression, "alias")
1428            values = f"VALUES{self.seg('')}{args}"
1429            values = (
1430                f"({values})"
1431                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1432                else values
1433            )
1434            return f"{values} AS {alias}" if alias else values
1435
1436        # Converts `VALUES...` expression into a series of select unions.
1437        expression = expression.copy()
1438        alias_node = expression.args.get("alias")
1439        column_names = alias_node and alias_node.columns
1440
1441        selects: t.List[exp.Subqueryable] = []
1442
1443        for i, tup in enumerate(expression.expressions):
1444            row = tup.expressions
1445
1446            if i == 0 and column_names:
1447                row = [
1448                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1449                ]
1450
1451            selects.append(exp.Select(expressions=row))
1452
1453        if self.pretty:
1454            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1455            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1456            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1457            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1458            return self.subquery_sql(
1459                subqueryable.subquery(alias_node and alias_node.this, copy=False)
1460            )
1461
1462        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
1463        unions = " UNION ALL ".join(self.sql(select) for select in selects)
1464        return f"({unions}){alias}"
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
1466    def var_sql(self, expression: exp.Var) -> str:
1467        return self.sql(expression, "this")
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
1469    def into_sql(self, expression: exp.Into) -> str:
1470        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1471        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1472        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
1474    def from_sql(self, expression: exp.From) -> str:
1475        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
1477    def group_sql(self, expression: exp.Group) -> str:
1478        group_by = self.op_expressions("GROUP BY", expression)
1479
1480        if expression.args.get("all"):
1481            return f"{group_by} ALL"
1482
1483        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1484        grouping_sets = (
1485            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1486        )
1487
1488        cube = expression.args.get("cube", [])
1489        if seq_get(cube, 0) is True:
1490            return f"{group_by}{self.seg('WITH CUBE')}"
1491        else:
1492            cube_sql = self.expressions(expression, key="cube", indent=False)
1493            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1494
1495        rollup = expression.args.get("rollup", [])
1496        if seq_get(rollup, 0) is True:
1497            return f"{group_by}{self.seg('WITH ROLLUP')}"
1498        else:
1499            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1500            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1501
1502        groupings = csv(
1503            grouping_sets,
1504            cube_sql,
1505            rollup_sql,
1506            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
1507            sep=self.GROUPINGS_SEP,
1508        )
1509
1510        if expression.args.get("expressions") and groupings:
1511            group_by = f"{group_by}{self.GROUPINGS_SEP}"
1512
1513        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
1515    def having_sql(self, expression: exp.Having) -> str:
1516        this = self.indent(self.sql(expression, "this"))
1517        return f"{self.seg('HAVING')}{self.sep()}{this}"
def connect_sql(self, expression: sqlglot.expressions.Connect) -> str:
1519    def connect_sql(self, expression: exp.Connect) -> str:
1520        start = self.sql(expression, "start")
1521        start = self.seg(f"START WITH {start}") if start else ""
1522        connect = self.sql(expression, "connect")
1523        connect = self.seg(f"CONNECT BY {connect}")
1524        return start + connect
def prior_sql(self, expression: sqlglot.expressions.Prior) -> str:
1526    def prior_sql(self, expression: exp.Prior) -> str:
1527        return f"PRIOR {self.sql(expression, 'this')}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
1529    def join_sql(self, expression: exp.Join) -> str:
1530        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
1531            side = None
1532        else:
1533            side = expression.side
1534
1535        op_sql = " ".join(
1536            op
1537            for op in (
1538                expression.method,
1539                "GLOBAL" if expression.args.get("global") else None,
1540                side,
1541                expression.kind,
1542                expression.hint if self.JOIN_HINTS else None,
1543            )
1544            if op
1545        )
1546        on_sql = self.sql(expression, "on")
1547        using = expression.args.get("using")
1548
1549        if not on_sql and using:
1550            on_sql = csv(*(self.sql(column) for column in using))
1551
1552        this_sql = self.sql(expression, "this")
1553
1554        if on_sql:
1555            on_sql = self.indent(on_sql, skip_first=True)
1556            space = self.seg(" " * self.pad) if self.pretty else " "
1557            if using:
1558                on_sql = f"{space}USING ({on_sql})"
1559            else:
1560                on_sql = f"{space}ON {on_sql}"
1561        elif not op_sql:
1562            return f", {this_sql}"
1563
1564        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
1565        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str:
1567    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1568        args = self.expressions(expression, flat=True)
1569        args = f"({args})" if len(args.split(",")) > 1 else args
1570        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
1572    def lateral_sql(self, expression: exp.Lateral) -> str:
1573        this = self.sql(expression, "this")
1574
1575        if isinstance(expression.this, exp.Subquery):
1576            return f"LATERAL {this}"
1577
1578        if expression.args.get("view"):
1579            alias = expression.args["alias"]
1580            columns = self.expressions(alias, key="columns", flat=True)
1581            table = f" {alias.name}" if alias.name else ""
1582            columns = f" AS {columns}" if columns else ""
1583            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1584            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1585
1586        alias = self.sql(expression, "alias")
1587        alias = f" AS {alias}" if alias else ""
1588        return f"LATERAL {this}{alias}"
def limit_sql(self, expression: sqlglot.expressions.Limit, top: bool = False) -> str:
1590    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
1591        this = self.sql(expression, "this")
1592        args = ", ".join(
1593            sql
1594            for sql in (
1595                self.sql(expression, "offset"),
1596                self.sql(expression, "expression"),
1597            )
1598            if sql
1599        )
1600        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
1602    def offset_sql(self, expression: exp.Offset) -> str:
1603        this = self.sql(expression, "this")
1604        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str:
1606    def setitem_sql(self, expression: exp.SetItem) -> str:
1607        kind = self.sql(expression, "kind")
1608        kind = f"{kind} " if kind else ""
1609        this = self.sql(expression, "this")
1610        expressions = self.expressions(expression)
1611        collate = self.sql(expression, "collate")
1612        collate = f" COLLATE {collate}" if collate else ""
1613        global_ = "GLOBAL " if expression.args.get("global") else ""
1614        return f"{global_}{kind}{this}{expressions}{collate}"
def set_sql(self, expression: sqlglot.expressions.Set) -> str:
1616    def set_sql(self, expression: exp.Set) -> str:
1617        expressions = (
1618            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
1619        )
1620        tag = " TAG" if expression.args.get("tag") else ""
1621        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str:
1623    def pragma_sql(self, expression: exp.Pragma) -> str:
1624        return f"PRAGMA {self.sql(expression, 'this')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
1626    def lock_sql(self, expression: exp.Lock) -> str:
1627        if not self.LOCKING_READS_SUPPORTED:
1628            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1629            return ""
1630
1631        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
1632        expressions = self.expressions(expression, flat=True)
1633        expressions = f" OF {expressions}" if expressions else ""
1634        wait = expression.args.get("wait")
1635
1636        if wait is not None:
1637            if isinstance(wait, exp.Literal):
1638                wait = f" WAIT {self.sql(wait)}"
1639            else:
1640                wait = " NOWAIT" if wait else " SKIP LOCKED"
1641
1642        return f"{lock_type}{expressions}{wait or ''}"
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
1644    def literal_sql(self, expression: exp.Literal) -> str:
1645        text = expression.this or ""
1646        if expression.is_string:
1647            text = f"{self.QUOTE_START}{self.escape_str(text)}{self.QUOTE_END}"
1648        return text
def escape_str(self, text: str) -> str:
1650    def escape_str(self, text: str) -> str:
1651        text = text.replace(self.QUOTE_END, self._escaped_quote_end)
1652        if self.UNESCAPED_SEQUENCE_TABLE:
1653            text = text.translate(self.UNESCAPED_SEQUENCE_TABLE)
1654        elif self.pretty:
1655            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1656        return text
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
1658    def loaddata_sql(self, expression: exp.LoadData) -> str:
1659        local = " LOCAL" if expression.args.get("local") else ""
1660        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1661        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1662        this = f" INTO TABLE {self.sql(expression, 'this')}"
1663        partition = self.sql(expression, "partition")
1664        partition = f" {partition}" if partition else ""
1665        input_format = self.sql(expression, "input_format")
1666        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1667        serde = self.sql(expression, "serde")
1668        serde = f" SERDE {serde}" if serde else ""
1669        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
1671    def null_sql(self, *_) -> str:
1672        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
1674    def boolean_sql(self, expression: exp.Boolean) -> str:
1675        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
1677    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1678        this = self.sql(expression, "this")
1679        this = f"{this} " if this else this
1680        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
1682    def cluster_sql(self, expression: exp.Cluster) -> str:
1683        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
1685    def distribute_sql(self, expression: exp.Distribute) -> str:
1686        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
1688    def sort_sql(self, expression: exp.Sort) -> str:
1689        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
1691    def ordered_sql(self, expression: exp.Ordered) -> str:
1692        desc = expression.args.get("desc")
1693        asc = not desc
1694
1695        nulls_first = expression.args.get("nulls_first")
1696        nulls_last = not nulls_first
1697        nulls_are_large = self.NULL_ORDERING == "nulls_are_large"
1698        nulls_are_small = self.NULL_ORDERING == "nulls_are_small"
1699        nulls_are_last = self.NULL_ORDERING == "nulls_are_last"
1700
1701        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
1702        nulls_sort_change = ""
1703        if nulls_first and (
1704            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
1705        ):
1706            nulls_sort_change = " NULLS FIRST"
1707        elif (
1708            nulls_last
1709            and ((asc and nulls_are_small) or (desc and nulls_are_large))
1710            and not nulls_are_last
1711        ):
1712            nulls_sort_change = " NULLS LAST"
1713
1714        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
1715            self.unsupported(
1716                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
1717            )
1718            nulls_sort_change = ""
1719
1720        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
1722    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
1723        partition = self.partition_by_sql(expression)
1724        order = self.sql(expression, "order")
1725        measures = self.expressions(expression, key="measures")
1726        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
1727        rows = self.sql(expression, "rows")
1728        rows = self.seg(rows) if rows else ""
1729        after = self.sql(expression, "after")
1730        after = self.seg(after) if after else ""
1731        pattern = self.sql(expression, "pattern")
1732        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
1733        definition_sqls = [
1734            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
1735            for definition in expression.args.get("define", [])
1736        ]
1737        definitions = self.expressions(sqls=definition_sqls)
1738        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
1739        body = "".join(
1740            (
1741                partition,
1742                order,
1743                measures,
1744                rows,
1745                after,
1746                pattern,
1747                define,
1748            )
1749        )
1750        alias = self.sql(expression, "alias")
1751        alias = f" {alias}" if alias else ""
1752        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
1754    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
1755        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
1756
1757        # If the limit is generated as TOP, we need to ensure it's not generated twice
1758        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
1759
1760        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
1761            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
1762        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
1763            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
1764
1765        fetch = isinstance(limit, exp.Fetch)
1766
1767        offset_limit_modifiers = (
1768            self.offset_limit_modifiers(expression, fetch, limit)
1769            if with_offset_limit_modifiers
1770            else []
1771        )
1772
1773        return csv(
1774            *sqls,
1775            *[self.sql(join) for join in expression.args.get("joins") or []],
1776            self.sql(expression, "connect"),
1777            self.sql(expression, "match"),
1778            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
1779            self.sql(expression, "where"),
1780            self.sql(expression, "group"),
1781            self.sql(expression, "having"),
1782            *self.after_having_modifiers(expression),
1783            self.sql(expression, "order"),
1784            *offset_limit_modifiers,
1785            *self.after_limit_modifiers(expression),
1786            sep="",
1787        )
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
1789    def offset_limit_modifiers(
1790        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
1791    ) -> t.List[str]:
1792        return [
1793            self.sql(expression, "offset") if fetch else self.sql(limit),
1794            self.sql(limit) if fetch else self.sql(expression, "offset"),
1795        ]
def after_having_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
1797    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
1798        return [
1799            self.sql(expression, "qualify"),
1800            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
1801            if expression.args.get("windows")
1802            else "",
1803            self.sql(expression, "distribute"),
1804            self.sql(expression, "sort"),
1805            self.sql(expression, "cluster"),
1806        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
1808    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
1809        locks = self.expressions(expression, key="locks", sep=" ")
1810        locks = f" {locks}" if locks else ""
1811        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
1813    def select_sql(self, expression: exp.Select) -> str:
1814        hint = self.sql(expression, "hint")
1815        distinct = self.sql(expression, "distinct")
1816        distinct = f" {distinct}" if distinct else ""
1817        kind = self.sql(expression, "kind").upper()
1818        limit = expression.args.get("limit")
1819        top = (
1820            self.limit_sql(limit, top=True)
1821            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
1822            else ""
1823        )
1824
1825        expressions = self.expressions(expression)
1826
1827        if kind:
1828            if kind in self.SELECT_KINDS:
1829                kind = f" AS {kind}"
1830            else:
1831                if kind == "STRUCT":
1832                    expressions = self.expressions(
1833                        sqls=[
1834                            self.sql(
1835                                exp.Struct(
1836                                    expressions=[
1837                                        exp.column(e.output_name).eq(
1838                                            e.this if isinstance(e, exp.Alias) else e
1839                                        )
1840                                        for e in expression.expressions
1841                                    ]
1842                                )
1843                            )
1844                        ]
1845                    )
1846                kind = ""
1847
1848        expressions = f"{self.sep()}{expressions}" if expressions else expressions
1849        sql = self.query_modifiers(
1850            expression,
1851            f"SELECT{top}{hint}{distinct}{kind}{expressions}",
1852            self.sql(expression, "into", comment=False),
1853            self.sql(expression, "from", comment=False),
1854        )
1855        return self.prepend_ctes(expression, sql)
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
1857    def schema_sql(self, expression: exp.Schema) -> str:
1858        this = self.sql(expression, "this")
1859        this = f"{this} " if this else ""
1860        sql = self.schema_columns_sql(expression)
1861        return f"{this}{sql}"
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
1863    def schema_columns_sql(self, expression: exp.Schema) -> str:
1864        return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
1866    def star_sql(self, expression: exp.Star) -> str:
1867        except_ = self.expressions(expression, key="except", flat=True)
1868        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
1869        replace = self.expressions(expression, key="replace", flat=True)
1870        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
1871        return f"*{except_}{replace}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
1873    def parameter_sql(self, expression: exp.Parameter) -> str:
1874        this = self.sql(expression, "this")
1875        return f"{self.PARAMETER_TOKEN}{this}" if self.SUPPORTS_PARAMETERS else this
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
1877    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
1878        this = self.sql(expression, "this")
1879        kind = expression.text("kind")
1880        if kind:
1881            kind = f"{kind}."
1882        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
1884    def placeholder_sql(self, expression: exp.Placeholder) -> str:
1885        return f":{expression.name}" if expression.name else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
1887    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
1888        alias = self.sql(expression, "alias")
1889        alias = f"{sep}{alias}" if alias else ""
1890
1891        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1892        pivots = f" {pivots}" if pivots else ""
1893
1894        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
1895        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
1897    def qualify_sql(self, expression: exp.Qualify) -> str:
1898        this = self.indent(self.sql(expression, "this"))
1899        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def union_sql(self, expression: sqlglot.expressions.Union) -> str:
1901    def union_sql(self, expression: exp.Union) -> str:
1902        return self.prepend_ctes(
1903            expression,
1904            self.set_operation(expression, self.union_op(expression)),
1905        )
def union_op(self, expression: sqlglot.expressions.Union) -> str:
1907    def union_op(self, expression: exp.Union) -> str:
1908        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
1909        kind = kind if expression.args.get("distinct") else " ALL"
1910        by_name = " BY NAME" if expression.args.get("by_name") else ""
1911        return f"UNION{kind}{by_name}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
1913    def unnest_sql(self, expression: exp.Unnest) -> str:
1914        args = self.expressions(expression, flat=True)
1915
1916        alias = expression.args.get("alias")
1917        offset = expression.args.get("offset")
1918
1919        if self.UNNEST_WITH_ORDINALITY:
1920            if alias and isinstance(offset, exp.Expression):
1921                alias = alias.copy()
1922                alias.append("columns", offset.copy())
1923
1924        if alias and self.UNNEST_COLUMN_ONLY:
1925            columns = alias.columns
1926            alias = self.sql(columns[0]) if columns else ""
1927        else:
1928            alias = self.sql(alias)
1929
1930        alias = f" AS {alias}" if alias else alias
1931        if self.UNNEST_WITH_ORDINALITY:
1932            suffix = f" WITH ORDINALITY{alias}" if offset else alias
1933        else:
1934            if isinstance(offset, exp.Expression):
1935                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
1936            elif offset:
1937                suffix = f"{alias} WITH OFFSET"
1938            else:
1939                suffix = alias
1940
1941        return f"UNNEST({args}){suffix}"
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
1943    def where_sql(self, expression: exp.Where) -> str:
1944        this = self.indent(self.sql(expression, "this"))
1945        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
1947    def window_sql(self, expression: exp.Window) -> str:
1948        this = self.sql(expression, "this")
1949        partition = self.partition_by_sql(expression)
1950        order = expression.args.get("order")
1951        order = self.order_sql(order, flat=True) if order else ""
1952        spec = self.sql(expression, "spec")
1953        alias = self.sql(expression, "alias")
1954        over = self.sql(expression, "over") or "OVER"
1955
1956        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
1957
1958        first = expression.args.get("first")
1959        if first is None:
1960            first = ""
1961        else:
1962            first = "FIRST" if first else "LAST"
1963
1964        if not partition and not order and not spec and alias:
1965            return f"{this} {alias}"
1966
1967        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
1968        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
1970    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
1971        partition = self.expressions(expression, key="partition_by", flat=True)
1972        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
1974    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
1975        kind = self.sql(expression, "kind")
1976        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
1977        end = (
1978            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
1979            or "CURRENT ROW"
1980        )
1981        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
1983    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
1984        this = self.sql(expression, "this")
1985        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
1986        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
1988    def between_sql(self, expression: exp.Between) -> str:
1989        this = self.sql(expression, "this")
1990        low = self.sql(expression, "low")
1991        high = self.sql(expression, "high")
1992        return f"{this} BETWEEN {low} AND {high}"
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
1994    def bracket_sql(self, expression: exp.Bracket) -> str:
1995        expressions = apply_index_offset(expression.this, expression.expressions, self.INDEX_OFFSET)
1996        expressions_sql = ", ".join(self.sql(e) for e in expressions)
1997
1998        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def safebracket_sql(self, expression: sqlglot.expressions.SafeBracket) -> str:
2000    def safebracket_sql(self, expression: exp.SafeBracket) -> str:
2001        return self.bracket_sql(expression)
def all_sql(self, expression: sqlglot.expressions.All) -> str:
2003    def all_sql(self, expression: exp.All) -> str:
2004        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
2006    def any_sql(self, expression: exp.Any) -> str:
2007        this = self.sql(expression, "this")
2008        if isinstance(expression.this, exp.Subqueryable):
2009            this = self.wrap(this)
2010        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
2012    def exists_sql(self, expression: exp.Exists) -> str:
2013        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
2015    def case_sql(self, expression: exp.Case) -> str:
2016        this = self.sql(expression, "this")
2017        statements = [f"CASE {this}" if this else "CASE"]
2018
2019        for e in expression.args["ifs"]:
2020            statements.append(f"WHEN {self.sql(e, 'this')}")
2021            statements.append(f"THEN {self.sql(e, 'true')}")
2022
2023        default = self.sql(expression, "default")
2024
2025        if default:
2026            statements.append(f"ELSE {default}")
2027
2028        statements.append("END")
2029
2030        if self.pretty and self.text_width(statements) > self.max_text_width:
2031            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2032
2033        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
2035    def constraint_sql(self, expression: exp.Constraint) -> str:
2036        this = self.sql(expression, "this")
2037        expressions = self.expressions(expression, flat=True)
2038        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
2040    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2041        order = expression.args.get("order")
2042        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2043        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
2045    def extract_sql(self, expression: exp.Extract) -> str:
2046        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2047        expression_sql = self.sql(expression, "expression")
2048        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
2050    def trim_sql(self, expression: exp.Trim) -> str:
2051        trim_type = self.sql(expression, "position")
2052
2053        if trim_type == "LEADING":
2054            return self.func("LTRIM", expression.this)
2055        elif trim_type == "TRAILING":
2056            return self.func("RTRIM", expression.this)
2057        else:
2058            return self.func("TRIM", expression.this, expression.expression)
def safeconcat_sql(self, expression: sqlglot.expressions.SafeConcat) -> str:
2060    def safeconcat_sql(self, expression: exp.SafeConcat) -> str:
2061        expressions = expression.expressions
2062        if self.STRICT_STRING_CONCAT:
2063            expressions = (exp.cast(e, "text") for e in expressions)
2064        return self.func("CONCAT", *expressions)
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
2066    def check_sql(self, expression: exp.Check) -> str:
2067        this = self.sql(expression, key="this")
2068        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2070    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2071        expressions = self.expressions(expression, flat=True)
2072        reference = self.sql(expression, "reference")
2073        reference = f" {reference}" if reference else ""
2074        delete = self.sql(expression, "delete")
2075        delete = f" ON DELETE {delete}" if delete else ""
2076        update = self.sql(expression, "update")
2077        update = f" ON UPDATE {update}" if update else ""
2078        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2080    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2081        expressions = self.expressions(expression, flat=True)
2082        options = self.expressions(expression, key="options", flat=True, sep=" ")
2083        options = f" {options}" if options else ""
2084        return f"PRIMARY KEY ({expressions}){options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
2086    def if_sql(self, expression: exp.If) -> str:
2087        expression = expression.copy()
2088        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
2090    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2091        modifier = expression.args.get("modifier")
2092        modifier = f" {modifier}" if modifier else ""
2093        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
2095    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2096        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str:
2098    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2099        return f"{self.sql(expression, 'this')} FORMAT JSON"
def jsonobject_sql(self, expression: sqlglot.expressions.JSONObject) -> str:
2101    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
2102        null_handling = expression.args.get("null_handling")
2103        null_handling = f" {null_handling}" if null_handling else ""
2104        unique_keys = expression.args.get("unique_keys")
2105        if unique_keys is not None:
2106            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2107        else:
2108            unique_keys = ""
2109        return_type = self.sql(expression, "return_type")
2110        return_type = f" RETURNING {return_type}" if return_type else ""
2111        encoding = self.sql(expression, "encoding")
2112        encoding = f" ENCODING {encoding}" if encoding else ""
2113        return self.func(
2114            "JSON_OBJECT",
2115            *expression.expressions,
2116            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2117        )
def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str:
2119    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2120        null_handling = expression.args.get("null_handling")
2121        null_handling = f" {null_handling}" if null_handling else ""
2122        return_type = self.sql(expression, "return_type")
2123        return_type = f" RETURNING {return_type}" if return_type else ""
2124        strict = " STRICT" if expression.args.get("strict") else ""
2125        return self.func(
2126            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2127        )
def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str:
2129    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2130        this = self.sql(expression, "this")
2131        order = self.sql(expression, "order")
2132        null_handling = expression.args.get("null_handling")
2133        null_handling = f" {null_handling}" if null_handling else ""
2134        return_type = self.sql(expression, "return_type")
2135        return_type = f" RETURNING {return_type}" if return_type else ""
2136        strict = " STRICT" if expression.args.get("strict") else ""
2137        return self.func(
2138            "JSON_ARRAYAGG",
2139            this,
2140            suffix=f"{order}{null_handling}{return_type}{strict})",
2141        )
def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str:
2143    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2144        this = self.sql(expression, "this")
2145        kind = self.sql(expression, "kind")
2146        kind = f" {kind}" if kind else ""
2147        path = self.sql(expression, "path")
2148        path = f" PATH {path}" if path else ""
2149        return f"{this}{kind}{path}"
def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str:
2151    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2152        this = self.sql(expression, "this")
2153        path = self.sql(expression, "path")
2154        path = f", {path}" if path else ""
2155        error_handling = expression.args.get("error_handling")
2156        error_handling = f" {error_handling}" if error_handling else ""
2157        empty_handling = expression.args.get("empty_handling")
2158        empty_handling = f" {empty_handling}" if empty_handling else ""
2159        columns = f" COLUMNS ({self.expressions(expression, skip_first=True)})"
2160        return self.func(
2161            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling}{columns})"
2162        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
2164    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2165        this = self.sql(expression, "this")
2166        kind = self.sql(expression, "kind")
2167        path = self.sql(expression, "path")
2168        path = f" {path}" if path else ""
2169        as_json = " AS JSON" if expression.args.get("as_json") else ""
2170        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
2172    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2173        this = self.sql(expression, "this")
2174        path = self.sql(expression, "path")
2175        path = f", {path}" if path else ""
2176        expressions = self.expressions(expression)
2177        with_ = (
2178            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2179            if expressions
2180            else ""
2181        )
2182        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
2184    def in_sql(self, expression: exp.In) -> str:
2185        query = expression.args.get("query")
2186        unnest = expression.args.get("unnest")
2187        field = expression.args.get("field")
2188        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2189
2190        if query:
2191            in_sql = self.wrap(query)
2192        elif unnest:
2193            in_sql = self.in_unnest_op(unnest)
2194        elif field:
2195            in_sql = self.sql(field)
2196        else:
2197            in_sql = f"({self.expressions(expression, flat=True)})"
2198
2199        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
2201    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2202        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
2204    def interval_sql(self, expression: exp.Interval) -> str:
2205        unit = self.sql(expression, "unit")
2206        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2207            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
2208        unit = f" {unit}" if unit else ""
2209
2210        if self.SINGLE_STRING_INTERVAL:
2211            this = expression.this.name if expression.this else ""
2212            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2213
2214        this = self.sql(expression, "this")
2215        if this:
2216            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2217            this = f" {this}" if unwrapped else f" ({this})"
2218
2219        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
2221    def return_sql(self, expression: exp.Return) -> str:
2222        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
2224    def reference_sql(self, expression: exp.Reference) -> str:
2225        this = self.sql(expression, "this")
2226        expressions = self.expressions(expression, flat=True)
2227        expressions = f"({expressions})" if expressions else ""
2228        options = self.expressions(expression, key="options", flat=True, sep=" ")
2229        options = f" {options}" if options else ""
2230        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
2232    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2233        return self.func(expression.name, *expression.expressions)
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
2235    def paren_sql(self, expression: exp.Paren) -> str:
2236        if isinstance(expression.unnest(), exp.Select):
2237            sql = self.wrap(expression)
2238        else:
2239            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2240            sql = f"({sql}{self.seg(')', sep='')}"
2241
2242        return self.prepend_ctes(expression, sql)
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
2244    def neg_sql(self, expression: exp.Neg) -> str:
2245        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2246        this_sql = self.sql(expression, "this")
2247        sep = " " if this_sql[0] == "-" else ""
2248        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
2250    def not_sql(self, expression: exp.Not) -> str:
2251        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
2253    def alias_sql(self, expression: exp.Alias) -> str:
2254        alias = self.sql(expression, "alias")
2255        alias = f" AS {alias}" if alias else ""
2256        return f"{self.sql(expression, 'this')}{alias}"
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
2258    def aliases_sql(self, expression: exp.Aliases) -> str:
2259        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2261    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2262        this = self.sql(expression, "this")
2263        zone = self.sql(expression, "zone")
2264        return f"{this} AT TIME ZONE {zone}"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
2266    def add_sql(self, expression: exp.Add) -> str:
2267        return self.binary(expression, "+")
def and_sql(self, expression: sqlglot.expressions.And) -> str:
2269    def and_sql(self, expression: exp.And) -> str:
2270        return self.connector_sql(expression, "AND")
def xor_sql(self, expression: sqlglot.expressions.Xor) -> str:
2272    def xor_sql(self, expression: exp.Xor) -> str:
2273        return self.connector_sql(expression, "XOR")
def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str:
2275    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2276        if not self.pretty:
2277            return self.binary(expression, op)
2278
2279        sqls = tuple(
2280            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2281            for i, e in enumerate(expression.flatten(unnest=False))
2282        )
2283
2284        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2285        return f"{sep}{op} ".join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
2287    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2288        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
2290    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2291        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
2293    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2294        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
2296    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2297        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
2299    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2300        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
2302    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2303        return self.binary(expression, "^")
def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2305    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2306        format_sql = self.sql(expression, "format")
2307        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2308        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')}{format_sql})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
2310    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2311        zone = self.sql(expression, "this")
2312        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
2314    def collate_sql(self, expression: exp.Collate) -> str:
2315        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
2317    def command_sql(self, expression: exp.Command) -> str:
2318        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
2320    def comment_sql(self, expression: exp.Comment) -> str:
2321        this = self.sql(expression, "this")
2322        kind = expression.args["kind"]
2323        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2324        expression_sql = self.sql(expression, "expression")
2325        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
2327    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2328        this = self.sql(expression, "this")
2329        delete = " DELETE" if expression.args.get("delete") else ""
2330        recompress = self.sql(expression, "recompress")
2331        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2332        to_disk = self.sql(expression, "to_disk")
2333        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2334        to_volume = self.sql(expression, "to_volume")
2335        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2336        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
2338    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2339        where = self.sql(expression, "where")
2340        group = self.sql(expression, "group")
2341        aggregates = self.expressions(expression, key="aggregates")
2342        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2343
2344        if not (where or group or aggregates) and len(expression.expressions) == 1:
2345            return f"TTL {self.expressions(expression, flat=True)}"
2346
2347        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
2349    def transaction_sql(self, expression: exp.Transaction) -> str:
2350        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
2352    def commit_sql(self, expression: exp.Commit) -> str:
2353        chain = expression.args.get("chain")
2354        if chain is not None:
2355            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2356
2357        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
2359    def rollback_sql(self, expression: exp.Rollback) -> str:
2360        savepoint = expression.args.get("savepoint")
2361        savepoint = f" TO {savepoint}" if savepoint else ""
2362        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
2364    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2365        this = self.sql(expression, "this")
2366
2367        dtype = self.sql(expression, "dtype")
2368        if dtype:
2369            collate = self.sql(expression, "collate")
2370            collate = f" COLLATE {collate}" if collate else ""
2371            using = self.sql(expression, "using")
2372            using = f" USING {using}" if using else ""
2373            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
2374
2375        default = self.sql(expression, "default")
2376        if default:
2377            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2378
2379        if not expression.args.get("drop"):
2380            self.unsupported("Unsupported ALTER COLUMN syntax")
2381
2382        return f"ALTER COLUMN {this} DROP DEFAULT"
def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str:
2384    def renametable_sql(self, expression: exp.RenameTable) -> str:
2385        if not self.RENAME_TABLE_WITH_DB:
2386            # Remove db from tables
2387            expression = expression.transform(
2388                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2389            )
2390        this = self.sql(expression, "this")
2391        return f"RENAME TO {this}"
def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2393    def altertable_sql(self, expression: exp.AlterTable) -> str:
2394        actions = expression.args["actions"]
2395
2396        if isinstance(actions[0], exp.ColumnDef):
2397            if self.ALTER_TABLE_ADD_COLUMN_KEYWORD:
2398                actions = self.expressions(
2399                    expression,
2400                    key="actions",
2401                    prefix="ADD COLUMN ",
2402                )
2403            else:
2404                actions = f"ADD {self.expressions(expression, key='actions')}"
2405        elif isinstance(actions[0], exp.Schema):
2406            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2407        elif isinstance(actions[0], exp.Delete):
2408            actions = self.expressions(expression, key="actions", flat=True)
2409        else:
2410            actions = self.expressions(expression, key="actions")
2411
2412        exists = " IF EXISTS" if expression.args.get("exists") else ""
2413        only = " ONLY" if expression.args.get("only") else ""
2414        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
2416    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2417        expressions = self.expressions(expression)
2418        exists = " IF EXISTS " if expression.args.get("exists") else " "
2419        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
2421    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2422        this = self.sql(expression, "this")
2423        expression_ = self.sql(expression, "expression")
2424        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
2425
2426        enforced = expression.args.get("enforced")
2427        if enforced is not None:
2428            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
2429
2430        return f"{add_constraint} {expression_}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
2432    def distinct_sql(self, expression: exp.Distinct) -> str:
2433        this = self.expressions(expression, flat=True)
2434        this = f" {this}" if this else ""
2435
2436        on = self.sql(expression, "on")
2437        on = f" ON {on}" if on else ""
2438        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
2440    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2441        return f"{self.sql(expression, 'this')} IGNORE NULLS"
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
2443    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2444        return f"{self.sql(expression, 'this')} RESPECT NULLS"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
2446    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2447        return self.sql(
2448            exp.Cast(
2449                this=exp.Div(this=expression.this.copy(), expression=expression.expression.copy()),
2450                to=exp.DataType(this=exp.DataType.Type.INT),
2451            )
2452        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
2454    def dpipe_sql(self, expression: exp.DPipe) -> str:
2455        return self.binary(expression, "||")
def safedpipe_sql(self, expression: sqlglot.expressions.SafeDPipe) -> str:
2457    def safedpipe_sql(self, expression: exp.SafeDPipe) -> str:
2458        if self.STRICT_STRING_CONCAT:
2459            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2460        return self.dpipe_sql(expression)
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
2462    def div_sql(self, expression: exp.Div) -> str:
2463        return self.binary(expression, "/")
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
2465    def overlaps_sql(self, expression: exp.Overlaps) -> str:
2466        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
2468    def distance_sql(self, expression: exp.Distance) -> str:
2469        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
2471    def dot_sql(self, expression: exp.Dot) -> str:
2472        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
2474    def eq_sql(self, expression: exp.EQ) -> str:
2475        return self.binary(expression, "=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
2477    def escape_sql(self, expression: exp.Escape) -> str:
2478        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
2480    def glob_sql(self, expression: exp.Glob) -> str:
2481        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
2483    def gt_sql(self, expression: exp.GT) -> str:
2484        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
2486    def gte_sql(self, expression: exp.GTE) -> str:
2487        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
2489    def ilike_sql(self, expression: exp.ILike) -> str:
2490        return self.binary(expression, "ILIKE")
def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str:
2492    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
2493        return self.binary(expression, "ILIKE ANY")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
2495    def is_sql(self, expression: exp.Is) -> str:
2496        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
2497            return self.sql(
2498                expression.this if expression.expression.this else exp.not_(expression.this)
2499            )
2500        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
2502    def like_sql(self, expression: exp.Like) -> str:
2503        return self.binary(expression, "LIKE")
def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str:
2505    def likeany_sql(self, expression: exp.LikeAny) -> str:
2506        return self.binary(expression, "LIKE ANY")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
2508    def similarto_sql(self, expression: exp.SimilarTo) -> str:
2509        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
2511    def lt_sql(self, expression: exp.LT) -> str:
2512        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
2514    def lte_sql(self, expression: exp.LTE) -> str:
2515        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
2517    def mod_sql(self, expression: exp.Mod) -> str:
2518        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
2520    def mul_sql(self, expression: exp.Mul) -> str:
2521        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
2523    def neq_sql(self, expression: exp.NEQ) -> str:
2524        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
2526    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
2527        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
2529    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
2530        return self.binary(expression, "IS DISTINCT FROM")
def or_sql(self, expression: sqlglot.expressions.Or) -> str:
2532    def or_sql(self, expression: exp.Or) -> str:
2533        return self.connector_sql(expression, "OR")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
2535    def slice_sql(self, expression: exp.Slice) -> str:
2536        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
2538    def sub_sql(self, expression: exp.Sub) -> str:
2539        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
2541    def trycast_sql(self, expression: exp.TryCast) -> str:
2542        return self.cast_sql(expression, safe_prefix="TRY_")
def log_sql(self, expression: sqlglot.expressions.Log) -> str:
2544    def log_sql(self, expression: exp.Log) -> str:
2545        args = list(expression.args.values())
2546        if not self.LOG_BASE_FIRST:
2547            args.reverse()
2548        return self.func("LOG", *args)
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
2550    def use_sql(self, expression: exp.Use) -> str:
2551        kind = self.sql(expression, "kind")
2552        kind = f" {kind}" if kind else ""
2553        this = self.sql(expression, "this")
2554        this = f" {this}" if this else ""
2555        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
2557    def binary(self, expression: exp.Binary, op: str) -> str:
2558        op = self.maybe_comment(op, comments=expression.comments)
2559        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
2561    def function_fallback_sql(self, expression: exp.Func) -> str:
2562        args = []
2563
2564        for key in expression.arg_types:
2565            arg_value = expression.args.get(key)
2566
2567            if isinstance(arg_value, list):
2568                for value in arg_value:
2569                    args.append(value)
2570            elif arg_value is not None:
2571                args.append(arg_value)
2572
2573        if self.normalize_functions:
2574            name = expression.sql_name()
2575        else:
2576            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
2577
2578        return self.func(name, *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
2580    def func(
2581        self,
2582        name: str,
2583        *args: t.Optional[exp.Expression | str],
2584        prefix: str = "(",
2585        suffix: str = ")",
2586    ) -> str:
2587        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
2589    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
2590        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
2591        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
2592            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
2593        return ", ".join(arg_sqls)
def text_width(self, args: Iterable) -> int:
2595    def text_width(self, args: t.Iterable) -> int:
2596        return sum(len(arg) for arg in args)
def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]:
2598    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
2599        return format_time(
2600            self.sql(expression, "format"), self.INVERSE_TIME_MAPPING, self.INVERSE_TIME_TRIE
2601        )
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:
2603    def expressions(
2604        self,
2605        expression: t.Optional[exp.Expression] = None,
2606        key: t.Optional[str] = None,
2607        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
2608        flat: bool = False,
2609        indent: bool = True,
2610        skip_first: bool = False,
2611        sep: str = ", ",
2612        prefix: str = "",
2613    ) -> str:
2614        expressions = expression.args.get(key or "expressions") if expression else sqls
2615
2616        if not expressions:
2617            return ""
2618
2619        if flat:
2620            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
2621
2622        num_sqls = len(expressions)
2623
2624        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
2625        pad = " " * self.pad
2626        stripped_sep = sep.strip()
2627
2628        result_sqls = []
2629        for i, e in enumerate(expressions):
2630            sql = self.sql(e, comment=False)
2631            if not sql:
2632                continue
2633
2634            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
2635
2636            if self.pretty:
2637                if self.leading_comma:
2638                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
2639                else:
2640                    result_sqls.append(
2641                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
2642                    )
2643            else:
2644                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
2645
2646        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
2647        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:
2649    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
2650        flat = flat or isinstance(expression.parent, exp.Properties)
2651        expressions_sql = self.expressions(expression, flat=flat)
2652        if flat:
2653            return f"{op} {expressions_sql}"
2654        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
2656    def naked_property(self, expression: exp.Property) -> str:
2657        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
2658        if not property_name:
2659            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
2660        return f"{property_name} {self.sql(expression, 'this')}"
def set_operation(self, expression: sqlglot.expressions.Expression, op: str) -> str:
2662    def set_operation(self, expression: exp.Expression, op: str) -> str:
2663        this = self.sql(expression, "this")
2664        op = self.seg(op)
2665        return self.query_modifiers(
2666            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
2667        )
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
2669    def tag_sql(self, expression: exp.Tag) -> str:
2670        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
2672    def token_sql(self, token_type: TokenType) -> str:
2673        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
2675    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
2676        this = self.sql(expression, "this")
2677        expressions = self.no_identify(self.expressions, expression)
2678        expressions = (
2679            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
2680        )
2681        return f"{this}{expressions}"
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
2683    def joinhint_sql(self, expression: exp.JoinHint) -> str:
2684        this = self.sql(expression, "this")
2685        expressions = self.expressions(expression, flat=True)
2686        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
2688    def kwarg_sql(self, expression: exp.Kwarg) -> str:
2689        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
2691    def when_sql(self, expression: exp.When) -> str:
2692        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
2693        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
2694        condition = self.sql(expression, "condition")
2695        condition = f" AND {condition}" if condition else ""
2696
2697        then_expression = expression.args.get("then")
2698        if isinstance(then_expression, exp.Insert):
2699            then = f"INSERT {self.sql(then_expression, 'this')}"
2700            if "expression" in then_expression.args:
2701                then += f" VALUES {self.sql(then_expression, 'expression')}"
2702        elif isinstance(then_expression, exp.Update):
2703            if isinstance(then_expression.args.get("expressions"), exp.Star):
2704                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
2705            else:
2706                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
2707        else:
2708            then = self.sql(then_expression)
2709        return f"WHEN {matched}{source}{condition} THEN {then}"
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
2711    def merge_sql(self, expression: exp.Merge) -> str:
2712        table = expression.this
2713        table_alias = ""
2714
2715        hints = table.args.get("hints")
2716        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
2717            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
2718            table = table.copy()
2719            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
2720
2721        this = self.sql(table)
2722        using = f"USING {self.sql(expression, 'using')}"
2723        on = f"ON {self.sql(expression, 'on')}"
2724        expressions = self.expressions(expression, sep=" ")
2725
2726        return f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
2728    def tochar_sql(self, expression: exp.ToChar) -> str:
2729        if expression.args.get("format"):
2730            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
2731
2732        return self.sql(exp.cast(expression.this, "text"))
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
2734    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
2735        this = self.sql(expression, "this")
2736        kind = self.sql(expression, "kind")
2737        settings_sql = self.expressions(expression, key="settings", sep=" ")
2738        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
2739        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
2741    def dictrange_sql(self, expression: exp.DictRange) -> str:
2742        this = self.sql(expression, "this")
2743        max = self.sql(expression, "max")
2744        min = self.sql(expression, "min")
2745        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
2747    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
2748        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
2750    def oncluster_sql(self, expression: exp.OnCluster) -> str:
2751        return ""
def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str:
2753    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
2754        expressions = self.expressions(expression, key="expressions", flat=True)
2755        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
2756        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
2757        buckets = self.sql(expression, "buckets")
2758        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str:
2760    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
2761        this = self.sql(expression, "this")
2762        having = self.sql(expression, "having")
2763
2764        if having:
2765            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
2766
2767        return self.func("ANY_VALUE", this)
def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str:
2769    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
2770        transform = self.func("TRANSFORM", *expression.expressions)
2771        row_format_before = self.sql(expression, "row_format_before")
2772        row_format_before = f" {row_format_before}" if row_format_before else ""
2773        record_writer = self.sql(expression, "record_writer")
2774        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
2775        using = f" USING {self.sql(expression, 'command_script')}"
2776        schema = self.sql(expression, "schema")
2777        schema = f" AS {schema}" if schema else ""
2778        row_format_after = self.sql(expression, "row_format_after")
2779        row_format_after = f" {row_format_after}" if row_format_after else ""
2780        record_reader = self.sql(expression, "record_reader")
2781        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
2782        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str:
2784    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
2785        key_block_size = self.sql(expression, "key_block_size")
2786        if key_block_size:
2787            return f"KEY_BLOCK_SIZE = {key_block_size}"
2788
2789        using = self.sql(expression, "using")
2790        if using:
2791            return f"USING {using}"
2792
2793        parser = self.sql(expression, "parser")
2794        if parser:
2795            return f"WITH PARSER {parser}"
2796
2797        comment = self.sql(expression, "comment")
2798        if comment:
2799            return f"COMMENT {comment}"
2800
2801        visible = expression.args.get("visible")
2802        if visible is not None:
2803            return "VISIBLE" if visible else "INVISIBLE"
2804
2805        engine_attr = self.sql(expression, "engine_attr")
2806        if engine_attr:
2807            return f"ENGINE_ATTRIBUTE = {engine_attr}"
2808
2809        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
2810        if secondary_engine_attr:
2811            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
2812
2813        self.unsupported("Unsupported index constraint option.")
2814        return ""
def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str:
2816    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
2817        kind = self.sql(expression, "kind")
2818        kind = f"{kind} INDEX" if kind else "INDEX"
2819        this = self.sql(expression, "this")
2820        this = f" {this}" if this else ""
2821        index_type = self.sql(expression, "index_type")
2822        index_type = f" USING {index_type}" if index_type else ""
2823        schema = self.sql(expression, "schema")
2824        schema = f" {schema}" if schema else ""
2825        options = self.expressions(expression, key="options", sep=" ")
2826        options = f" {options}" if options else ""
2827        return f"{kind}{this}{index_type}{schema}{options}"
def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str:
2829    def nvl2_sql(self, expression: exp.Nvl2) -> str:
2830        if self.NVL2_SUPPORTED:
2831            return self.function_fallback_sql(expression)
2832
2833        case = exp.Case().when(
2834            expression.this.is_(exp.null()).not_(copy=False),
2835            expression.args["true"].copy(),
2836            copy=False,
2837        )
2838        else_cond = expression.args.get("false")
2839        if else_cond:
2840            case.else_(else_cond.copy(), copy=False)
2841
2842        return self.sql(case)
def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str:
2844    def comprehension_sql(self, expression: exp.Comprehension) -> str:
2845        this = self.sql(expression, "this")
2846        expr = self.sql(expression, "expression")
2847        iterator = self.sql(expression, "iterator")
2848        condition = self.sql(expression, "condition")
2849        condition = f" IF {condition}" if condition else ""
2850        return f"{this} FOR {expr} IN {iterator}{condition}"
def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str:
2852    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
2853        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str:
2855    def opclass_sql(self, expression: exp.Opclass) -> str:
2856        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def cached_generator( cache: Optional[Dict[int, str]] = None) -> Callable[[sqlglot.expressions.Expression], str]:
2859def cached_generator(
2860    cache: t.Optional[t.Dict[int, str]] = None
2861) -> t.Callable[[exp.Expression], str]:
2862    """Returns a cached generator."""
2863    cache = {} if cache is None else cache
2864    generator = Generator(normalize=True, identify="safe")
2865    return lambda e: generator.generate(e, cache)

Returns a cached generator.