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

Returns a cached generator.