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

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

Returns a cached generator.