Edit on GitHub

sqlglot.generator

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

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)
303    def __init__(
304        self,
305        pretty: t.Optional[bool] = None,
306        identify: str | bool = False,
307        normalize: bool = False,
308        pad: int = 2,
309        indent: int = 2,
310        normalize_functions: t.Optional[str | bool] = None,
311        unsupported_level: ErrorLevel = ErrorLevel.WARN,
312        max_unsupported: int = 3,
313        leading_comma: bool = False,
314        max_text_width: int = 80,
315        comments: bool = True,
316    ):
317        import sqlglot
318
319        self.pretty = pretty if pretty is not None else sqlglot.pretty
320        self.identify = identify
321        self.normalize = normalize
322        self.pad = pad
323        self._indent = indent
324        self.unsupported_level = unsupported_level
325        self.max_unsupported = max_unsupported
326        self.leading_comma = leading_comma
327        self.max_text_width = max_text_width
328        self.comments = comments
329
330        # This is both a Dialect property and a Generator argument, so we prioritize the latter
331        self.normalize_functions = (
332            self.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
333        )
334
335        self.unsupported_messages: t.List[str] = []
336        self._escaped_quote_end: str = self.STRING_ESCAPE + self.QUOTE_END
337        self._escaped_identifier_end: str = self.IDENTIFIER_ESCAPE + self.IDENTIFIER_END
338        self._cache: t.Optional[t.Dict[int, str]] = None
def generate( self, expression: Optional[sqlglot.expressions.Expression], cache: Optional[Dict[int, str]] = None) -> str:
340    def generate(
341        self,
342        expression: t.Optional[exp.Expression],
343        cache: t.Optional[t.Dict[int, str]] = None,
344    ) -> str:
345        """
346        Generates the SQL string corresponding to the given syntax tree.
347
348        Args:
349            expression: The syntax tree.
350            cache: An optional sql string cache. This leverages the hash of an Expression
351                which can be slow to compute, so only use it if you set _hash on each node.
352
353        Returns:
354            The SQL string corresponding to `expression`.
355        """
356        if cache is not None:
357            self._cache = cache
358
359        self.unsupported_messages = []
360        sql = self.sql(expression).strip()
361        self._cache = None
362
363        if self.unsupported_level == ErrorLevel.IGNORE:
364            return sql
365
366        if self.unsupported_level == ErrorLevel.WARN:
367            for msg in self.unsupported_messages:
368                logger.warning(msg)
369        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
370            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
371
372        if self.pretty:
373            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
374        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:
376    def unsupported(self, message: str) -> None:
377        if self.unsupported_level == ErrorLevel.IMMEDIATE:
378            raise UnsupportedError(message)
379        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
381    def sep(self, sep: str = " ") -> str:
382        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
384    def seg(self, sql: str, sep: str = " ") -> str:
385        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
387    def pad_comment(self, comment: str) -> str:
388        comment = " " + comment if comment[0].strip() else comment
389        comment = comment + " " if comment[-1].strip() else comment
390        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None) -> str:
392    def maybe_comment(
393        self,
394        sql: str,
395        expression: t.Optional[exp.Expression] = None,
396        comments: t.Optional[t.List[str]] = None,
397    ) -> str:
398        comments = (
399            ((expression and expression.comments) if comments is None else comments)  # type: ignore
400            if self.comments
401            else None
402        )
403
404        if not comments or isinstance(expression, exp.Binary):
405            return sql
406
407        sep = "\n" if self.pretty else " "
408        comments_sql = sep.join(
409            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
410        )
411
412        if not comments_sql:
413            return sql
414
415        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
416            return (
417                f"{self.sep()}{comments_sql}{sql}"
418                if sql[0].isspace()
419                else f"{comments_sql}{self.sep()}{sql}"
420            )
421
422        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
424    def wrap(self, expression: exp.Expression | str) -> str:
425        this_sql = self.indent(
426            self.sql(expression)
427            if isinstance(expression, (exp.Select, exp.Union))
428            else self.sql(expression, "this"),
429            level=1,
430            pad=0,
431        )
432        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
434    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
435        original = self.identify
436        self.identify = False
437        result = func(*args, **kwargs)
438        self.identify = original
439        return result
def normalize_func(self, name: str) -> str:
441    def normalize_func(self, name: str) -> str:
442        if self.normalize_functions == "upper" or self.normalize_functions is True:
443            return name.upper()
444        if self.normalize_functions == "lower":
445            return name.lower()
446        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
448    def indent(
449        self,
450        sql: str,
451        level: int = 0,
452        pad: t.Optional[int] = None,
453        skip_first: bool = False,
454        skip_last: bool = False,
455    ) -> str:
456        if not self.pretty:
457            return sql
458
459        pad = self.pad if pad is None else pad
460        lines = sql.split("\n")
461
462        return "\n".join(
463            line
464            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
465            else f"{' ' * (level * self._indent + pad)}{line}"
466            for i, line in enumerate(lines)
467        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
469    def sql(
470        self,
471        expression: t.Optional[str | exp.Expression],
472        key: t.Optional[str] = None,
473        comment: bool = True,
474    ) -> str:
475        if not expression:
476            return ""
477
478        if isinstance(expression, str):
479            return expression
480
481        if key:
482            return self.sql(expression.args.get(key))
483
484        if self._cache is not None:
485            expression_id = hash(expression)
486
487            if expression_id in self._cache:
488                return self._cache[expression_id]
489
490        transform = self.TRANSFORMS.get(expression.__class__)
491
492        if callable(transform):
493            sql = transform(self, expression)
494        elif transform:
495            sql = transform
496        elif isinstance(expression, exp.Expression):
497            exp_handler_name = f"{expression.key}_sql"
498
499            if hasattr(self, exp_handler_name):
500                sql = getattr(self, exp_handler_name)(expression)
501            elif isinstance(expression, exp.Func):
502                sql = self.function_fallback_sql(expression)
503            elif isinstance(expression, exp.Property):
504                sql = self.property_sql(expression)
505            else:
506                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
507        else:
508            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
509
510        sql = self.maybe_comment(sql, expression) if self.comments and comment else sql
511
512        if self._cache is not None:
513            self._cache[expression_id] = sql
514        return sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
516    def uncache_sql(self, expression: exp.Uncache) -> str:
517        table = self.sql(expression, "this")
518        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
519        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
521    def cache_sql(self, expression: exp.Cache) -> str:
522        lazy = " LAZY" if expression.args.get("lazy") else ""
523        table = self.sql(expression, "this")
524        options = expression.args.get("options")
525        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
526        sql = self.sql(expression, "expression")
527        sql = f" AS{self.sep()}{sql}" if sql else ""
528        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
529        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
531    def characterset_sql(self, expression: exp.CharacterSet) -> str:
532        if isinstance(expression.parent, exp.Cast):
533            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
534        default = "DEFAULT " if expression.args.get("default") else ""
535        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
537    def column_sql(self, expression: exp.Column) -> str:
538        return ".".join(
539            self.sql(part)
540            for part in (
541                expression.args.get("catalog"),
542                expression.args.get("db"),
543                expression.args.get("table"),
544                expression.args.get("this"),
545            )
546            if part
547        )
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
549    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
550        this = self.sql(expression, "this")
551        this = f" {this}" if this else ""
552        position = self.sql(expression, "position")
553        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
555    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
556        column = self.sql(expression, "this")
557        kind = self.sql(expression, "kind")
558        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
559        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
560        kind = f"{sep}{kind}" if kind else ""
561        constraints = f" {constraints}" if constraints else ""
562        position = self.sql(expression, "position")
563        position = f" {position}" if position else ""
564
565        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
567    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
568        this = self.sql(expression, "this")
569        kind_sql = self.sql(expression, "kind").strip()
570        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def autoincrementcolumnconstraint_sql(self, _) -> str:
572    def autoincrementcolumnconstraint_sql(self, _) -> str:
573        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
575    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
576        if isinstance(expression.this, list):
577            this = self.wrap(self.expressions(expression, key="this", flat=True))
578        else:
579            this = self.sql(expression, "this")
580
581        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
583    def generatedasidentitycolumnconstraint_sql(
584        self, expression: exp.GeneratedAsIdentityColumnConstraint
585    ) -> str:
586        this = ""
587        if expression.this is not None:
588            on_null = "ON NULL " if expression.args.get("on_null") else ""
589            this = " ALWAYS " if expression.this else f" BY DEFAULT {on_null}"
590
591        start = expression.args.get("start")
592        start = f"START WITH {start}" if start else ""
593        increment = expression.args.get("increment")
594        increment = f" INCREMENT BY {increment}" if increment else ""
595        minvalue = expression.args.get("minvalue")
596        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
597        maxvalue = expression.args.get("maxvalue")
598        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
599        cycle = expression.args.get("cycle")
600        cycle_sql = ""
601
602        if cycle is not None:
603            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
604            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
605
606        sequence_opts = ""
607        if start or increment or cycle_sql:
608            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
609            sequence_opts = f" ({sequence_opts.strip()})"
610
611        expr = self.sql(expression, "expression")
612        expr = f"({expr})" if expr else "IDENTITY"
613
614        return f"GENERATED{this}AS {expr}{sequence_opts}"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
616    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
617        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
619    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
620        desc = expression.args.get("desc")
621        if desc is not None:
622            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
623        return f"PRIMARY KEY"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
625    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
626        this = self.sql(expression, "this")
627        this = f" {this}" if this else ""
628        return f"UNIQUE{this}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: dict[sqlglot.expressions.Properties.Location, list[sqlglot.expressions.Property]]) -> str:
630    def createable_sql(
631        self, expression: exp.Create, locations: dict[exp.Properties.Location, list[exp.Property]]
632    ) -> str:
633        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
635    def create_sql(self, expression: exp.Create) -> str:
636        kind = self.sql(expression, "kind").upper()
637        properties = expression.args.get("properties")
638        properties_locs = self.locate_properties(properties) if properties else {}
639
640        this = self.createable_sql(expression, properties_locs)
641
642        properties_sql = ""
643        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
644            exp.Properties.Location.POST_WITH
645        ):
646            properties_sql = self.sql(
647                exp.Properties(
648                    expressions=[
649                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
650                        *properties_locs[exp.Properties.Location.POST_WITH],
651                    ]
652                )
653            )
654
655        begin = " BEGIN" if expression.args.get("begin") else ""
656        expression_sql = self.sql(expression, "expression")
657        if expression_sql:
658            expression_sql = f"{begin}{self.sep()}{expression_sql}"
659
660            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
661                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
662                    postalias_props_sql = self.properties(
663                        exp.Properties(
664                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
665                        ),
666                        wrapped=False,
667                    )
668                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
669                else:
670                    expression_sql = f" AS{expression_sql}"
671
672        postindex_props_sql = ""
673        if properties_locs.get(exp.Properties.Location.POST_INDEX):
674            postindex_props_sql = self.properties(
675                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
676                wrapped=False,
677                prefix=" ",
678            )
679
680        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
681        indexes = f" {indexes}" if indexes else ""
682        index_sql = indexes + postindex_props_sql
683
684        replace = " OR REPLACE" if expression.args.get("replace") else ""
685        unique = " UNIQUE" if expression.args.get("unique") else ""
686
687        postcreate_props_sql = ""
688        if properties_locs.get(exp.Properties.Location.POST_CREATE):
689            postcreate_props_sql = self.properties(
690                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
691                sep=" ",
692                prefix=" ",
693                wrapped=False,
694            )
695
696        modifiers = "".join((replace, unique, postcreate_props_sql))
697
698        postexpression_props_sql = ""
699        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
700            postexpression_props_sql = self.properties(
701                exp.Properties(
702                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
703                ),
704                sep=" ",
705                prefix=" ",
706                wrapped=False,
707            )
708
709        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
710        no_schema_binding = (
711            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
712        )
713
714        clone = self.sql(expression, "clone")
715        clone = f" {clone}" if clone else ""
716
717        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
718        return self.prepend_ctes(expression, expression_sql)
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
720    def clone_sql(self, expression: exp.Clone) -> str:
721        this = self.sql(expression, "this")
722        when = self.sql(expression, "when")
723
724        if when:
725            kind = self.sql(expression, "kind")
726            expr = self.sql(expression, "expression")
727            return f"CLONE {this} {when} ({kind} => {expr})"
728
729        return f"CLONE {this}"
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
731    def describe_sql(self, expression: exp.Describe) -> str:
732        return f"DESCRIBE {self.sql(expression, 'this')}"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
734    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
735        with_ = self.sql(expression, "with")
736        if with_:
737            sql = f"{with_}{self.sep()}{sql}"
738        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
740    def with_sql(self, expression: exp.With) -> str:
741        sql = self.expressions(expression, flat=True)
742        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
743
744        return f"WITH {recursive}{sql}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
746    def cte_sql(self, expression: exp.CTE) -> str:
747        alias = self.sql(expression, "alias")
748        return f"{alias} AS {self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
750    def tablealias_sql(self, expression: exp.TableAlias) -> str:
751        alias = self.sql(expression, "this")
752        columns = self.expressions(expression, key="columns", flat=True)
753        columns = f"({columns})" if columns else ""
754        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
756    def bitstring_sql(self, expression: exp.BitString) -> str:
757        this = self.sql(expression, "this")
758        if self.BIT_START:
759            return f"{self.BIT_START}{this}{self.BIT_END}"
760        return f"{int(this, 2)}"
def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str:
762    def hexstring_sql(self, expression: exp.HexString) -> str:
763        this = self.sql(expression, "this")
764        if self.HEX_START:
765            return f"{self.HEX_START}{this}{self.HEX_END}"
766        return f"{int(this, 16)}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
768    def bytestring_sql(self, expression: exp.ByteString) -> str:
769        this = self.sql(expression, "this")
770        if self.BYTE_START:
771            return f"{self.BYTE_START}{this}{self.BYTE_END}"
772        return this
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
774    def rawstring_sql(self, expression: exp.RawString) -> str:
775        if self.RAW_START:
776            return f"{self.RAW_START}{expression.name}{self.RAW_END}"
777        return self.sql(exp.Literal.string(expression.name.replace("\\", "\\\\")))
def datatypesize_sql(self, expression: sqlglot.expressions.DataTypeSize) -> str:
779    def datatypesize_sql(self, expression: exp.DataTypeSize) -> str:
780        this = self.sql(expression, "this")
781        specifier = self.sql(expression, "expression")
782        specifier = f" {specifier}" if specifier else ""
783        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
785    def datatype_sql(self, expression: exp.DataType) -> str:
786        type_value = expression.this
787        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
788        nested = ""
789        interior = self.expressions(expression, flat=True)
790        values = ""
791        if interior:
792            if expression.args.get("nested"):
793                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
794                if expression.args.get("values") is not None:
795                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
796                    values = self.expressions(expression, key="values", flat=True)
797                    values = f"{delimiters[0]}{values}{delimiters[1]}"
798            else:
799                nested = f"({interior})"
800
801        return f"{type_sql}{nested}{values}"
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
803    def directory_sql(self, expression: exp.Directory) -> str:
804        local = "LOCAL " if expression.args.get("local") else ""
805        row_format = self.sql(expression, "row_format")
806        row_format = f" {row_format}" if row_format else ""
807        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
809    def delete_sql(self, expression: exp.Delete) -> str:
810        this = self.sql(expression, "this")
811        this = f" FROM {this}" if this else ""
812        using_sql = (
813            f" USING {self.expressions(expression, key='using', sep=', USING ')}"
814            if expression.args.get("using")
815            else ""
816        )
817        where_sql = self.sql(expression, "where")
818        returning = self.sql(expression, "returning")
819        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
820        return self.prepend_ctes(expression, sql)
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
822    def drop_sql(self, expression: exp.Drop) -> str:
823        this = self.sql(expression, "this")
824        kind = expression.args["kind"]
825        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
826        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
827        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
828        cascade = " CASCADE" if expression.args.get("cascade") else ""
829        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
830        purge = " PURGE" if expression.args.get("purge") else ""
831        return (
832            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
833        )
def except_sql(self, expression: sqlglot.expressions.Except) -> str:
835    def except_sql(self, expression: exp.Except) -> str:
836        return self.prepend_ctes(
837            expression,
838            self.set_operation(expression, self.except_op(expression)),
839        )
def except_op(self, expression: sqlglot.expressions.Except) -> str:
841    def except_op(self, expression: exp.Except) -> str:
842        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
844    def fetch_sql(self, expression: exp.Fetch) -> str:
845        direction = expression.args.get("direction")
846        direction = f" {direction.upper()}" if direction else ""
847        count = expression.args.get("count")
848        count = f" {count}" if count else ""
849        if expression.args.get("percent"):
850            count = f"{count} PERCENT"
851        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
852        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
854    def filter_sql(self, expression: exp.Filter) -> str:
855        this = self.sql(expression, "this")
856        where = self.sql(expression, "expression")[1:]  # where has a leading space
857        return f"{this} FILTER({where})"
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
859    def hint_sql(self, expression: exp.Hint) -> str:
860        if self.sql(expression, "this"):
861            self.unsupported("Hints are not supported")
862        return ""
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
864    def index_sql(self, expression: exp.Index) -> str:
865        unique = "UNIQUE " if expression.args.get("unique") else ""
866        primary = "PRIMARY " if expression.args.get("primary") else ""
867        amp = "AMP " if expression.args.get("amp") else ""
868        name = f"{expression.name} " if expression.name else ""
869        table = self.sql(expression, "table")
870        table = f"{self.INDEX_ON} {table} " if table else ""
871        using = self.sql(expression, "using")
872        using = f"USING {using} " if using else ""
873        index = "INDEX " if not table else ""
874        columns = self.expressions(expression, key="columns", flat=True)
875        columns = f"({columns})" if columns else ""
876        partition_by = self.expressions(expression, key="partition_by", flat=True)
877        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
878        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
880    def identifier_sql(self, expression: exp.Identifier) -> str:
881        text = expression.name
882        lower = text.lower()
883        text = lower if self.normalize and not expression.quoted else text
884        text = text.replace(self.IDENTIFIER_END, self._escaped_identifier_end)
885        if (
886            expression.quoted
887            or should_identify(text, self.identify)
888            or lower in self.RESERVED_KEYWORDS
889            or (not self.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
890        ):
891            text = f"{self.IDENTIFIER_START}{text}{self.IDENTIFIER_END}"
892        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
894    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
895        input_format = self.sql(expression, "input_format")
896        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
897        output_format = self.sql(expression, "output_format")
898        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
899        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
901    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
902        string = self.sql(exp.Literal.string(expression.name))
903        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
905    def partition_sql(self, expression: exp.Partition) -> str:
906        return f"PARTITION({self.expressions(expression)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
908    def properties_sql(self, expression: exp.Properties) -> str:
909        root_properties = []
910        with_properties = []
911
912        for p in expression.expressions:
913            p_loc = self.PROPERTIES_LOCATION[p.__class__]
914            if p_loc == exp.Properties.Location.POST_WITH:
915                with_properties.append(p)
916            elif p_loc == exp.Properties.Location.POST_SCHEMA:
917                root_properties.append(p)
918
919        return self.root_properties(
920            exp.Properties(expressions=root_properties)
921        ) + self.with_properties(exp.Properties(expressions=with_properties))
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
923    def root_properties(self, properties: exp.Properties) -> str:
924        if properties.expressions:
925            return self.sep() + self.expressions(properties, indent=False, sep=" ")
926        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
928    def properties(
929        self,
930        properties: exp.Properties,
931        prefix: str = "",
932        sep: str = ", ",
933        suffix: str = "",
934        wrapped: bool = True,
935    ) -> str:
936        if properties.expressions:
937            expressions = self.expressions(properties, sep=sep, indent=False)
938            expressions = self.wrap(expressions) if wrapped else expressions
939            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
940        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
942    def with_properties(self, properties: exp.Properties) -> str:
943        return self.properties(properties, prefix=self.seg("WITH"))
def locate_properties( self, properties: sqlglot.expressions.Properties) -> Dict[sqlglot.expressions.Properties.Location, list[sqlglot.expressions.Property]]:
945    def locate_properties(
946        self, properties: exp.Properties
947    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
948        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
949            key: [] for key in exp.Properties.Location
950        }
951
952        for p in properties.expressions:
953            p_loc = self.PROPERTIES_LOCATION[p.__class__]
954            if p_loc == exp.Properties.Location.POST_NAME:
955                properties_locs[exp.Properties.Location.POST_NAME].append(p)
956            elif p_loc == exp.Properties.Location.POST_INDEX:
957                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
958            elif p_loc == exp.Properties.Location.POST_SCHEMA:
959                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
960            elif p_loc == exp.Properties.Location.POST_WITH:
961                properties_locs[exp.Properties.Location.POST_WITH].append(p)
962            elif p_loc == exp.Properties.Location.POST_CREATE:
963                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
964            elif p_loc == exp.Properties.Location.POST_ALIAS:
965                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
966            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
967                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
968            elif p_loc == exp.Properties.Location.UNSUPPORTED:
969                self.unsupported(f"Unsupported property {p.key}")
970
971        return properties_locs
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
973    def property_sql(self, expression: exp.Property) -> str:
974        property_cls = expression.__class__
975        if property_cls == exp.Property:
976            return f"{expression.name}={self.sql(expression, 'value')}"
977
978        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
979        if not property_name:
980            self.unsupported(f"Unsupported property {expression.key}")
981
982        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
984    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
985        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
986        options = f" {options}" if options else ""
987        return f"LIKE {self.sql(expression, 'this')}{options}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
989    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
990        no = "NO " if expression.args.get("no") else ""
991        protection = " PROTECTION" if expression.args.get("protection") else ""
992        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
 994    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
 995        no = "NO " if expression.args.get("no") else ""
 996        local = expression.args.get("local")
 997        local = f"{local} " if local else ""
 998        dual = "DUAL " if expression.args.get("dual") else ""
 999        before = "BEFORE " if expression.args.get("before") else ""
1000        after = "AFTER " if expression.args.get("after") else ""
1001        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1003    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1004        freespace = self.sql(expression, "this")
1005        percent = " PERCENT" if expression.args.get("percent") else ""
1006        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1008    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1009        if expression.args.get("default"):
1010            property = "DEFAULT"
1011        elif expression.args.get("on"):
1012            property = "ON"
1013        else:
1014            property = "OFF"
1015        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1017    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1018        if expression.args.get("no"):
1019            return "NO MERGEBLOCKRATIO"
1020        if expression.args.get("default"):
1021            return "DEFAULT MERGEBLOCKRATIO"
1022
1023        percent = " PERCENT" if expression.args.get("percent") else ""
1024        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1026    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1027        default = expression.args.get("default")
1028        minimum = expression.args.get("minimum")
1029        maximum = expression.args.get("maximum")
1030        if default or minimum or maximum:
1031            if default:
1032                prop = "DEFAULT"
1033            elif minimum:
1034                prop = "MINIMUM"
1035            else:
1036                prop = "MAXIMUM"
1037            return f"{prop} DATABLOCKSIZE"
1038        units = expression.args.get("units")
1039        units = f" {units}" if units else ""
1040        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1042    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1043        autotemp = expression.args.get("autotemp")
1044        always = expression.args.get("always")
1045        default = expression.args.get("default")
1046        manual = expression.args.get("manual")
1047        never = expression.args.get("never")
1048
1049        if autotemp is not None:
1050            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1051        elif always:
1052            prop = "ALWAYS"
1053        elif default:
1054            prop = "DEFAULT"
1055        elif manual:
1056            prop = "MANUAL"
1057        elif never:
1058            prop = "NEVER"
1059        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1061    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1062        no = expression.args.get("no")
1063        no = " NO" if no else ""
1064        concurrent = expression.args.get("concurrent")
1065        concurrent = " CONCURRENT" if concurrent else ""
1066
1067        for_ = ""
1068        if expression.args.get("for_all"):
1069            for_ = " FOR ALL"
1070        elif expression.args.get("for_insert"):
1071            for_ = " FOR INSERT"
1072        elif expression.args.get("for_none"):
1073            for_ = " FOR NONE"
1074        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1076    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1077        kind = expression.args.get("kind")
1078        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1079        for_or_in = expression.args.get("for_or_in")
1080        lock_type = expression.args.get("lock_type")
1081        override = " OVERRIDE" if expression.args.get("override") else ""
1082        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1084    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1085        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1086        statistics = expression.args.get("statistics")
1087        statistics_sql = ""
1088        if statistics is not None:
1089            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1090        return f"{data_sql}{statistics_sql}"
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1092    def insert_sql(self, expression: exp.Insert) -> str:
1093        overwrite = expression.args.get("overwrite")
1094
1095        if isinstance(expression.this, exp.Directory):
1096            this = "OVERWRITE " if overwrite else "INTO "
1097        else:
1098            this = "OVERWRITE TABLE " if overwrite else "INTO "
1099
1100        alternative = expression.args.get("alternative")
1101        alternative = f" OR {alternative} " if alternative else " "
1102        this = f"{this}{self.sql(expression, 'this')}"
1103
1104        exists = " IF EXISTS " if expression.args.get("exists") else " "
1105        partition_sql = (
1106            self.sql(expression, "partition") if expression.args.get("partition") else ""
1107        )
1108        expression_sql = self.sql(expression, "expression")
1109        conflict = self.sql(expression, "conflict")
1110        returning = self.sql(expression, "returning")
1111        sep = self.sep() if partition_sql else ""
1112        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{conflict}{returning}"
1113        return self.prepend_ctes(expression, sql)
def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str:
1115    def intersect_sql(self, expression: exp.Intersect) -> str:
1116        return self.prepend_ctes(
1117            expression,
1118            self.set_operation(expression, self.intersect_op(expression)),
1119        )
def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str:
1121    def intersect_op(self, expression: exp.Intersect) -> str:
1122        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1124    def introducer_sql(self, expression: exp.Introducer) -> str:
1125        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1127    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1128        return expression.name.upper()
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1130    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1131        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1132        constraint = self.sql(expression, "constraint")
1133        if constraint:
1134            constraint = f"ON CONSTRAINT {constraint}"
1135        key = self.expressions(expression, key="key", flat=True)
1136        do = "" if expression.args.get("duplicate") else " DO "
1137        nothing = "NOTHING" if expression.args.get("nothing") else ""
1138        expressions = self.expressions(expression, flat=True)
1139        if expressions:
1140            expressions = f"UPDATE SET {expressions}"
1141        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1143    def returning_sql(self, expression: exp.Returning) -> str:
1144        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1146    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1147        fields = expression.args.get("fields")
1148        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1149        escaped = expression.args.get("escaped")
1150        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1151        items = expression.args.get("collection_items")
1152        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1153        keys = expression.args.get("map_keys")
1154        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1155        lines = expression.args.get("lines")
1156        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1157        null = expression.args.get("null")
1158        null = f" NULL DEFINED AS {null}" if null else ""
1159        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
1161    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1162        table = ".".join(
1163            part
1164            for part in [
1165                self.sql(expression, "catalog"),
1166                self.sql(expression, "db"),
1167                self.sql(expression, "this"),
1168            ]
1169            if part
1170        )
1171
1172        alias = self.sql(expression, "alias")
1173        alias = f"{sep}{alias}" if alias else ""
1174        hints = self.expressions(expression, key="hints", flat=True)
1175        hints = f" WITH ({hints})" if hints and self.TABLE_HINTS else ""
1176        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1177        pivots = f" {pivots}" if pivots else ""
1178        joins = self.expressions(expression, key="joins", sep="")
1179        laterals = self.expressions(expression, key="laterals", sep="")
1180        system_time = expression.args.get("system_time")
1181        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
1182
1183        return f"{table}{system_time}{alias}{hints}{pivots}{joins}{laterals}"
def tablesample_sql( self, expression: sqlglot.expressions.TableSample, seed_prefix: str = 'SEED', sep=' AS ') -> str:
1185    def tablesample_sql(
1186        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
1187    ) -> str:
1188        if self.ALIAS_POST_TABLESAMPLE and expression.this.alias:
1189            table = expression.this.copy()
1190            table.set("alias", None)
1191            this = self.sql(table)
1192            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1193        else:
1194            this = self.sql(expression, "this")
1195            alias = ""
1196        method = self.sql(expression, "method")
1197        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1198        numerator = self.sql(expression, "bucket_numerator")
1199        denominator = self.sql(expression, "bucket_denominator")
1200        field = self.sql(expression, "bucket_field")
1201        field = f" ON {field}" if field else ""
1202        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1203        percent = self.sql(expression, "percent")
1204        percent = f"{percent} PERCENT" if percent else ""
1205        rows = self.sql(expression, "rows")
1206        rows = f"{rows} ROWS" if rows else ""
1207        size = self.sql(expression, "size")
1208        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
1209            size = f"{size} PERCENT"
1210        seed = self.sql(expression, "seed")
1211        seed = f" {seed_prefix} ({seed})" if seed else ""
1212        kind = expression.args.get("kind", "TABLESAMPLE")
1213        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
1215    def pivot_sql(self, expression: exp.Pivot) -> str:
1216        expressions = self.expressions(expression, flat=True)
1217
1218        if expression.this:
1219            this = self.sql(expression, "this")
1220            on = f"{self.seg('ON')} {expressions}"
1221            using = self.expressions(expression, key="using", flat=True)
1222            using = f"{self.seg('USING')} {using}" if using else ""
1223            group = self.sql(expression, "group")
1224            return f"PIVOT {this}{on}{using}{group}"
1225
1226        alias = self.sql(expression, "alias")
1227        alias = f" AS {alias}" if alias else ""
1228        unpivot = expression.args.get("unpivot")
1229        direction = "UNPIVOT" if unpivot else "PIVOT"
1230        field = self.sql(expression, "field")
1231        return f"{direction}({expressions} FOR {field}){alias}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
1233    def tuple_sql(self, expression: exp.Tuple) -> str:
1234        return f"({self.expressions(expression, flat=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
1236    def update_sql(self, expression: exp.Update) -> str:
1237        this = self.sql(expression, "this")
1238        set_sql = self.expressions(expression, flat=True)
1239        from_sql = self.sql(expression, "from")
1240        where_sql = self.sql(expression, "where")
1241        returning = self.sql(expression, "returning")
1242        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
1243        return self.prepend_ctes(expression, sql)
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
1245    def values_sql(self, expression: exp.Values) -> str:
1246        args = self.expressions(expression)
1247        alias = self.sql(expression, "alias")
1248        values = f"VALUES{self.seg('')}{args}"
1249        values = (
1250            f"({values})"
1251            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1252            else values
1253        )
1254        return f"{values} AS {alias}" if alias else values
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
1256    def var_sql(self, expression: exp.Var) -> str:
1257        return self.sql(expression, "this")
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
1259    def into_sql(self, expression: exp.Into) -> str:
1260        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1261        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1262        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
1264    def from_sql(self, expression: exp.From) -> str:
1265        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
1267    def group_sql(self, expression: exp.Group) -> str:
1268        group_by = self.op_expressions("GROUP BY", expression)
1269        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1270        grouping_sets = (
1271            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1272        )
1273
1274        cube = expression.args.get("cube", [])
1275        if seq_get(cube, 0) is True:
1276            return f"{group_by}{self.seg('WITH CUBE')}"
1277        else:
1278            cube_sql = self.expressions(expression, key="cube", indent=False)
1279            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1280
1281        rollup = expression.args.get("rollup", [])
1282        if seq_get(rollup, 0) is True:
1283            return f"{group_by}{self.seg('WITH ROLLUP')}"
1284        else:
1285            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1286            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1287
1288        groupings = csv(
1289            grouping_sets,
1290            cube_sql,
1291            rollup_sql,
1292            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
1293            sep=self.GROUPINGS_SEP,
1294        )
1295
1296        if expression.args.get("expressions") and groupings:
1297            group_by = f"{group_by}{self.GROUPINGS_SEP}"
1298
1299        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
1301    def having_sql(self, expression: exp.Having) -> str:
1302        this = self.indent(self.sql(expression, "this"))
1303        return f"{self.seg('HAVING')}{self.sep()}{this}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
1305    def join_sql(self, expression: exp.Join) -> str:
1306        op_sql = " ".join(
1307            op
1308            for op in (
1309                expression.method,
1310                "GLOBAL" if expression.args.get("global") else None,
1311                expression.side,
1312                expression.kind,
1313                expression.hint if self.JOIN_HINTS else None,
1314            )
1315            if op
1316        )
1317        on_sql = self.sql(expression, "on")
1318        using = expression.args.get("using")
1319
1320        if not on_sql and using:
1321            on_sql = csv(*(self.sql(column) for column in using))
1322
1323        this_sql = self.sql(expression, "this")
1324
1325        if on_sql:
1326            on_sql = self.indent(on_sql, skip_first=True)
1327            space = self.seg(" " * self.pad) if self.pretty else " "
1328            if using:
1329                on_sql = f"{space}USING ({on_sql})"
1330            else:
1331                on_sql = f"{space}ON {on_sql}"
1332        elif not op_sql:
1333            return f", {this_sql}"
1334
1335        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
1336        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str:
1338    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1339        args = self.expressions(expression, flat=True)
1340        args = f"({args})" if len(args.split(",")) > 1 else args
1341        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
1343    def lateral_sql(self, expression: exp.Lateral) -> str:
1344        this = self.sql(expression, "this")
1345
1346        if isinstance(expression.this, exp.Subquery):
1347            return f"LATERAL {this}"
1348
1349        if expression.args.get("view"):
1350            alias = expression.args["alias"]
1351            columns = self.expressions(alias, key="columns", flat=True)
1352            table = f" {alias.name}" if alias.name else ""
1353            columns = f" AS {columns}" if columns else ""
1354            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1355            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1356
1357        alias = self.sql(expression, "alias")
1358        alias = f" AS {alias}" if alias else ""
1359        return f"LATERAL {this}{alias}"
def limit_sql(self, expression: sqlglot.expressions.Limit) -> str:
1361    def limit_sql(self, expression: exp.Limit) -> str:
1362        this = self.sql(expression, "this")
1363        args = ", ".join(
1364            sql
1365            for sql in (
1366                self.sql(expression, "offset"),
1367                self.sql(expression, "expression"),
1368            )
1369            if sql
1370        )
1371        return f"{this}{self.seg('LIMIT')} {args}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
1373    def offset_sql(self, expression: exp.Offset) -> str:
1374        this = self.sql(expression, "this")
1375        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str:
1377    def setitem_sql(self, expression: exp.SetItem) -> str:
1378        kind = self.sql(expression, "kind")
1379        kind = f"{kind} " if kind else ""
1380        this = self.sql(expression, "this")
1381        expressions = self.expressions(expression)
1382        collate = self.sql(expression, "collate")
1383        collate = f" COLLATE {collate}" if collate else ""
1384        global_ = "GLOBAL " if expression.args.get("global") else ""
1385        return f"{global_}{kind}{this}{expressions}{collate}"
def set_sql(self, expression: sqlglot.expressions.Set) -> str:
1387    def set_sql(self, expression: exp.Set) -> str:
1388        expressions = (
1389            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
1390        )
1391        return f"SET{expressions}"
def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str:
1393    def pragma_sql(self, expression: exp.Pragma) -> str:
1394        return f"PRAGMA {self.sql(expression, 'this')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
1396    def lock_sql(self, expression: exp.Lock) -> str:
1397        if not self.LOCKING_READS_SUPPORTED:
1398            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1399            return ""
1400
1401        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
1402        expressions = self.expressions(expression, flat=True)
1403        expressions = f" OF {expressions}" if expressions else ""
1404        wait = expression.args.get("wait")
1405
1406        if wait is not None:
1407            if isinstance(wait, exp.Literal):
1408                wait = f" WAIT {self.sql(wait)}"
1409            else:
1410                wait = " NOWAIT" if wait else " SKIP LOCKED"
1411
1412        return f"{lock_type}{expressions}{wait or ''}"
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
1414    def literal_sql(self, expression: exp.Literal) -> str:
1415        text = expression.this or ""
1416        if expression.is_string:
1417            text = text.replace(self.QUOTE_END, self._escaped_quote_end)
1418            if self.pretty:
1419                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1420            text = f"{self.QUOTE_START}{text}{self.QUOTE_END}"
1421        return text
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
1423    def loaddata_sql(self, expression: exp.LoadData) -> str:
1424        local = " LOCAL" if expression.args.get("local") else ""
1425        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1426        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1427        this = f" INTO TABLE {self.sql(expression, 'this')}"
1428        partition = self.sql(expression, "partition")
1429        partition = f" {partition}" if partition else ""
1430        input_format = self.sql(expression, "input_format")
1431        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1432        serde = self.sql(expression, "serde")
1433        serde = f" SERDE {serde}" if serde else ""
1434        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
1436    def null_sql(self, *_) -> str:
1437        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
1439    def boolean_sql(self, expression: exp.Boolean) -> str:
1440        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
1442    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1443        this = self.sql(expression, "this")
1444        this = f"{this} " if this else this
1445        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
1447    def cluster_sql(self, expression: exp.Cluster) -> str:
1448        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
1450    def distribute_sql(self, expression: exp.Distribute) -> str:
1451        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
1453    def sort_sql(self, expression: exp.Sort) -> str:
1454        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
1456    def ordered_sql(self, expression: exp.Ordered) -> str:
1457        desc = expression.args.get("desc")
1458        asc = not desc
1459
1460        nulls_first = expression.args.get("nulls_first")
1461        nulls_last = not nulls_first
1462        nulls_are_large = self.NULL_ORDERING == "nulls_are_large"
1463        nulls_are_small = self.NULL_ORDERING == "nulls_are_small"
1464        nulls_are_last = self.NULL_ORDERING == "nulls_are_last"
1465
1466        sort_order = " DESC" if desc else ""
1467        nulls_sort_change = ""
1468        if nulls_first and (
1469            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
1470        ):
1471            nulls_sort_change = " NULLS FIRST"
1472        elif (
1473            nulls_last
1474            and ((asc and nulls_are_small) or (desc and nulls_are_large))
1475            and not nulls_are_last
1476        ):
1477            nulls_sort_change = " NULLS LAST"
1478
1479        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
1480            self.unsupported(
1481                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
1482            )
1483            nulls_sort_change = ""
1484
1485        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
1487    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
1488        partition = self.partition_by_sql(expression)
1489        order = self.sql(expression, "order")
1490        measures = self.expressions(expression, key="measures")
1491        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
1492        rows = self.sql(expression, "rows")
1493        rows = self.seg(rows) if rows else ""
1494        after = self.sql(expression, "after")
1495        after = self.seg(after) if after else ""
1496        pattern = self.sql(expression, "pattern")
1497        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
1498        definition_sqls = [
1499            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
1500            for definition in expression.args.get("define", [])
1501        ]
1502        definitions = self.expressions(sqls=definition_sqls)
1503        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
1504        body = "".join(
1505            (
1506                partition,
1507                order,
1508                measures,
1509                rows,
1510                after,
1511                pattern,
1512                define,
1513            )
1514        )
1515        alias = self.sql(expression, "alias")
1516        alias = f" {alias}" if alias else ""
1517        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
1519    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
1520        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
1521
1522        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
1523            limit = exp.Limit(expression=limit.args.get("count"))
1524        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
1525            limit = exp.Fetch(direction="FIRST", count=limit.expression)
1526
1527        fetch = isinstance(limit, exp.Fetch)
1528
1529        return csv(
1530            *sqls,
1531            *[self.sql(join) for join in expression.args.get("joins") or []],
1532            self.sql(expression, "match"),
1533            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
1534            self.sql(expression, "where"),
1535            self.sql(expression, "group"),
1536            self.sql(expression, "having"),
1537            *self.after_having_modifiers(expression),
1538            self.sql(expression, "order"),
1539            *self.offset_limit_modifiers(expression, fetch, limit),
1540            *self.after_limit_modifiers(expression),
1541            sep="",
1542        )
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
1544    def offset_limit_modifiers(
1545        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
1546    ) -> t.List[str]:
1547        return [
1548            self.sql(expression, "offset") if fetch else self.sql(limit),
1549            self.sql(limit) if fetch else self.sql(expression, "offset"),
1550        ]
def after_having_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
1552    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
1553        return [
1554            self.sql(expression, "qualify"),
1555            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
1556            if expression.args.get("windows")
1557            else "",
1558        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
1560    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
1561        locks = self.expressions(expression, key="locks", sep=" ")
1562        locks = f" {locks}" if locks else ""
1563        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
1565    def select_sql(self, expression: exp.Select) -> str:
1566        hint = self.sql(expression, "hint")
1567        distinct = self.sql(expression, "distinct")
1568        distinct = f" {distinct}" if distinct else ""
1569        kind = expression.args.get("kind")
1570        kind = f" AS {kind}" if kind else ""
1571        expressions = self.expressions(expression)
1572        expressions = f"{self.sep()}{expressions}" if expressions else expressions
1573        sql = self.query_modifiers(
1574            expression,
1575            f"SELECT{hint}{distinct}{kind}{expressions}",
1576            self.sql(expression, "into", comment=False),
1577            self.sql(expression, "from", comment=False),
1578        )
1579        return self.prepend_ctes(expression, sql)
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
1581    def schema_sql(self, expression: exp.Schema) -> str:
1582        this = self.sql(expression, "this")
1583        this = f"{this} " if this else ""
1584        sql = self.schema_columns_sql(expression)
1585        return f"{this}{sql}"
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
1587    def schema_columns_sql(self, expression: exp.Schema) -> str:
1588        return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
1590    def star_sql(self, expression: exp.Star) -> str:
1591        except_ = self.expressions(expression, key="except", flat=True)
1592        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
1593        replace = self.expressions(expression, key="replace", flat=True)
1594        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
1595        return f"*{except_}{replace}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
1597    def parameter_sql(self, expression: exp.Parameter) -> str:
1598        this = self.sql(expression, "this")
1599        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
1600        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
1602    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
1603        this = self.sql(expression, "this")
1604        kind = expression.text("kind")
1605        if kind:
1606            kind = f"{kind}."
1607        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
1609    def placeholder_sql(self, expression: exp.Placeholder) -> str:
1610        return f":{expression.name}" if expression.name else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
1612    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
1613        alias = self.sql(expression, "alias")
1614        alias = f"{sep}{alias}" if alias else ""
1615
1616        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1617        pivots = f" {pivots}" if pivots else ""
1618
1619        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
1620        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
1622    def qualify_sql(self, expression: exp.Qualify) -> str:
1623        this = self.indent(self.sql(expression, "this"))
1624        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def union_sql(self, expression: sqlglot.expressions.Union) -> str:
1626    def union_sql(self, expression: exp.Union) -> str:
1627        return self.prepend_ctes(
1628            expression,
1629            self.set_operation(expression, self.union_op(expression)),
1630        )
def union_op(self, expression: sqlglot.expressions.Union) -> str:
1632    def union_op(self, expression: exp.Union) -> str:
1633        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
1634        kind = kind if expression.args.get("distinct") else " ALL"
1635        return f"UNION{kind}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
1637    def unnest_sql(self, expression: exp.Unnest) -> str:
1638        args = self.expressions(expression, flat=True)
1639        alias = expression.args.get("alias")
1640        if alias and self.UNNEST_COLUMN_ONLY:
1641            columns = alias.columns
1642            alias = self.sql(columns[0]) if columns else ""
1643        else:
1644            alias = self.sql(expression, "alias")
1645        alias = f" AS {alias}" if alias else alias
1646        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
1647        offset = expression.args.get("offset")
1648        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
1649        return f"UNNEST({args}){ordinality}{alias}{offset}"
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
1651    def where_sql(self, expression: exp.Where) -> str:
1652        this = self.indent(self.sql(expression, "this"))
1653        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
1655    def window_sql(self, expression: exp.Window) -> str:
1656        this = self.sql(expression, "this")
1657        partition = self.partition_by_sql(expression)
1658        order = expression.args.get("order")
1659        order = self.order_sql(order, flat=True) if order else ""
1660        spec = self.sql(expression, "spec")
1661        alias = self.sql(expression, "alias")
1662        over = self.sql(expression, "over") or "OVER"
1663
1664        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
1665
1666        first = expression.args.get("first")
1667        if first is None:
1668            first = ""
1669        else:
1670            first = "FIRST" if first else "LAST"
1671
1672        if not partition and not order and not spec and alias:
1673            return f"{this} {alias}"
1674
1675        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
1676        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
1678    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
1679        partition = self.expressions(expression, key="partition_by", flat=True)
1680        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
1682    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
1683        kind = self.sql(expression, "kind")
1684        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
1685        end = (
1686            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
1687            or "CURRENT ROW"
1688        )
1689        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
1691    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
1692        this = self.sql(expression, "this")
1693        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
1694        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
1696    def between_sql(self, expression: exp.Between) -> str:
1697        this = self.sql(expression, "this")
1698        low = self.sql(expression, "low")
1699        high = self.sql(expression, "high")
1700        return f"{this} BETWEEN {low} AND {high}"
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
1702    def bracket_sql(self, expression: exp.Bracket) -> str:
1703        expressions = apply_index_offset(expression.this, expression.expressions, self.INDEX_OFFSET)
1704        expressions_sql = ", ".join(self.sql(e) for e in expressions)
1705
1706        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
1708    def all_sql(self, expression: exp.All) -> str:
1709        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
1711    def any_sql(self, expression: exp.Any) -> str:
1712        this = self.sql(expression, "this")
1713        if isinstance(expression.this, exp.Subqueryable):
1714            this = self.wrap(this)
1715        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
1717    def exists_sql(self, expression: exp.Exists) -> str:
1718        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
1720    def case_sql(self, expression: exp.Case) -> str:
1721        this = self.sql(expression, "this")
1722        statements = [f"CASE {this}" if this else "CASE"]
1723
1724        for e in expression.args["ifs"]:
1725            statements.append(f"WHEN {self.sql(e, 'this')}")
1726            statements.append(f"THEN {self.sql(e, 'true')}")
1727
1728        default = self.sql(expression, "default")
1729
1730        if default:
1731            statements.append(f"ELSE {default}")
1732
1733        statements.append("END")
1734
1735        if self.pretty and self.text_width(statements) > self.max_text_width:
1736            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
1737
1738        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
1740    def constraint_sql(self, expression: exp.Constraint) -> str:
1741        this = self.sql(expression, "this")
1742        expressions = self.expressions(expression, flat=True)
1743        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
1745    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
1746        order = expression.args.get("order")
1747        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
1748        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
1750    def extract_sql(self, expression: exp.Extract) -> str:
1751        this = self.sql(expression, "this")
1752        expression_sql = self.sql(expression, "expression")
1753        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
1755    def trim_sql(self, expression: exp.Trim) -> str:
1756        trim_type = self.sql(expression, "position")
1757
1758        if trim_type == "LEADING":
1759            return self.func("LTRIM", expression.this)
1760        elif trim_type == "TRAILING":
1761            return self.func("RTRIM", expression.this)
1762        else:
1763            return self.func("TRIM", expression.this, expression.expression)
def safeconcat_sql(self, expression: sqlglot.expressions.SafeConcat) -> str:
1765    def safeconcat_sql(self, expression: exp.SafeConcat) -> str:
1766        expressions = expression.expressions
1767        if self.STRICT_STRING_CONCAT:
1768            expressions = (exp.cast(e, "text") for e in expressions)
1769        return self.func("CONCAT", *expressions)
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
1771    def check_sql(self, expression: exp.Check) -> str:
1772        this = self.sql(expression, key="this")
1773        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
1775    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
1776        expressions = self.expressions(expression, flat=True)
1777        reference = self.sql(expression, "reference")
1778        reference = f" {reference}" if reference else ""
1779        delete = self.sql(expression, "delete")
1780        delete = f" ON DELETE {delete}" if delete else ""
1781        update = self.sql(expression, "update")
1782        update = f" ON UPDATE {update}" if update else ""
1783        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
1785    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
1786        expressions = self.expressions(expression, flat=True)
1787        options = self.expressions(expression, key="options", flat=True, sep=" ")
1788        options = f" {options}" if options else ""
1789        return f"PRIMARY KEY ({expressions}){options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
1791    def if_sql(self, expression: exp.If) -> str:
1792        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
1794    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
1795        modifier = expression.args.get("modifier")
1796        modifier = f" {modifier}" if modifier else ""
1797        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
1799    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
1800        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
def jsonobject_sql(self, expression: sqlglot.expressions.JSONObject) -> str:
1802    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
1803        null_handling = expression.args.get("null_handling")
1804        null_handling = f" {null_handling}" if null_handling else ""
1805        unique_keys = expression.args.get("unique_keys")
1806        if unique_keys is not None:
1807            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
1808        else:
1809            unique_keys = ""
1810        return_type = self.sql(expression, "return_type")
1811        return_type = f" RETURNING {return_type}" if return_type else ""
1812        format_json = " FORMAT JSON" if expression.args.get("format_json") else ""
1813        encoding = self.sql(expression, "encoding")
1814        encoding = f" ENCODING {encoding}" if encoding else ""
1815        return self.func(
1816            "JSON_OBJECT",
1817            *expression.expressions,
1818            suffix=f"{null_handling}{unique_keys}{return_type}{format_json}{encoding})",
1819        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
1821    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
1822        this = self.sql(expression, "this")
1823        kind = self.sql(expression, "kind")
1824        path = self.sql(expression, "path")
1825        path = f" {path}" if path else ""
1826        as_json = " AS JSON" if expression.args.get("as_json") else ""
1827        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
1829    def openjson_sql(self, expression: exp.OpenJSON) -> str:
1830        this = self.sql(expression, "this")
1831        path = self.sql(expression, "path")
1832        path = f", {path}" if path else ""
1833        expressions = self.expressions(expression)
1834        with_ = (
1835            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
1836            if expressions
1837            else ""
1838        )
1839        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
1841    def in_sql(self, expression: exp.In) -> str:
1842        query = expression.args.get("query")
1843        unnest = expression.args.get("unnest")
1844        field = expression.args.get("field")
1845        is_global = " GLOBAL" if expression.args.get("is_global") else ""
1846
1847        if query:
1848            in_sql = self.wrap(query)
1849        elif unnest:
1850            in_sql = self.in_unnest_op(unnest)
1851        elif field:
1852            in_sql = self.sql(field)
1853        else:
1854            in_sql = f"({self.expressions(expression, flat=True)})"
1855
1856        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
1858    def in_unnest_op(self, unnest: exp.Unnest) -> str:
1859        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
1861    def interval_sql(self, expression: exp.Interval) -> str:
1862        unit = self.sql(expression, "unit")
1863        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
1864            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
1865        unit = f" {unit}" if unit else ""
1866
1867        if self.SINGLE_STRING_INTERVAL:
1868            this = expression.this.name if expression.this else ""
1869            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
1870
1871        this = self.sql(expression, "this")
1872        if this:
1873            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
1874            this = f" {this}" if unwrapped else f" ({this})"
1875
1876        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
1878    def return_sql(self, expression: exp.Return) -> str:
1879        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
1881    def reference_sql(self, expression: exp.Reference) -> str:
1882        this = self.sql(expression, "this")
1883        expressions = self.expressions(expression, flat=True)
1884        expressions = f"({expressions})" if expressions else ""
1885        options = self.expressions(expression, key="options", flat=True, sep=" ")
1886        options = f" {options}" if options else ""
1887        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
1889    def anonymous_sql(self, expression: exp.Anonymous) -> str:
1890        return self.func(expression.name, *expression.expressions)
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
1892    def paren_sql(self, expression: exp.Paren) -> str:
1893        if isinstance(expression.unnest(), exp.Select):
1894            sql = self.wrap(expression)
1895        else:
1896            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
1897            sql = f"({sql}{self.seg(')', sep='')}"
1898
1899        return self.prepend_ctes(expression, sql)
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
1901    def neg_sql(self, expression: exp.Neg) -> str:
1902        # This makes sure we don't convert "- - 5" to "--5", which is a comment
1903        this_sql = self.sql(expression, "this")
1904        sep = " " if this_sql[0] == "-" else ""
1905        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
1907    def not_sql(self, expression: exp.Not) -> str:
1908        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
1910    def alias_sql(self, expression: exp.Alias) -> str:
1911        alias = self.sql(expression, "alias")
1912        alias = f" AS {alias}" if alias else ""
1913        return f"{self.sql(expression, 'this')}{alias}"
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
1915    def aliases_sql(self, expression: exp.Aliases) -> str:
1916        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
1918    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
1919        this = self.sql(expression, "this")
1920        zone = self.sql(expression, "zone")
1921        return f"{this} AT TIME ZONE {zone}"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
1923    def add_sql(self, expression: exp.Add) -> str:
1924        return self.binary(expression, "+")
def and_sql(self, expression: sqlglot.expressions.And) -> str:
1926    def and_sql(self, expression: exp.And) -> str:
1927        return self.connector_sql(expression, "AND")
def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str:
1929    def connector_sql(self, expression: exp.Connector, op: str) -> str:
1930        if not self.pretty:
1931            return self.binary(expression, op)
1932
1933        sqls = tuple(
1934            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
1935            for i, e in enumerate(expression.flatten(unnest=False))
1936        )
1937
1938        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
1939        return f"{sep}{op} ".join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
1941    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
1942        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
1944    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
1945        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
1947    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
1948        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
1950    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
1951        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
1953    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
1954        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
1956    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
1957        return self.binary(expression, "^")
def cast_sql(self, expression: sqlglot.expressions.Cast) -> str:
1959    def cast_sql(self, expression: exp.Cast) -> str:
1960        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
1962    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
1963        zone = self.sql(expression, "this")
1964        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
1966    def collate_sql(self, expression: exp.Collate) -> str:
1967        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
1969    def command_sql(self, expression: exp.Command) -> str:
1970        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
1972    def comment_sql(self, expression: exp.Comment) -> str:
1973        this = self.sql(expression, "this")
1974        kind = expression.args["kind"]
1975        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1976        expression_sql = self.sql(expression, "expression")
1977        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
1979    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
1980        this = self.sql(expression, "this")
1981        delete = " DELETE" if expression.args.get("delete") else ""
1982        recompress = self.sql(expression, "recompress")
1983        recompress = f" RECOMPRESS {recompress}" if recompress else ""
1984        to_disk = self.sql(expression, "to_disk")
1985        to_disk = f" TO DISK {to_disk}" if to_disk else ""
1986        to_volume = self.sql(expression, "to_volume")
1987        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
1988        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
1990    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
1991        where = self.sql(expression, "where")
1992        group = self.sql(expression, "group")
1993        aggregates = self.expressions(expression, key="aggregates")
1994        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
1995
1996        if not (where or group or aggregates) and len(expression.expressions) == 1:
1997            return f"TTL {self.expressions(expression, flat=True)}"
1998
1999        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
2001    def transaction_sql(self, expression: exp.Transaction) -> str:
2002        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
2004    def commit_sql(self, expression: exp.Commit) -> str:
2005        chain = expression.args.get("chain")
2006        if chain is not None:
2007            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2008
2009        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
2011    def rollback_sql(self, expression: exp.Rollback) -> str:
2012        savepoint = expression.args.get("savepoint")
2013        savepoint = f" TO {savepoint}" if savepoint else ""
2014        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
2016    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2017        this = self.sql(expression, "this")
2018
2019        dtype = self.sql(expression, "dtype")
2020        if dtype:
2021            collate = self.sql(expression, "collate")
2022            collate = f" COLLATE {collate}" if collate else ""
2023            using = self.sql(expression, "using")
2024            using = f" USING {using}" if using else ""
2025            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
2026
2027        default = self.sql(expression, "default")
2028        if default:
2029            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2030
2031        if not expression.args.get("drop"):
2032            self.unsupported("Unsupported ALTER COLUMN syntax")
2033
2034        return f"ALTER COLUMN {this} DROP DEFAULT"
def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str:
2036    def renametable_sql(self, expression: exp.RenameTable) -> str:
2037        if not self.RENAME_TABLE_WITH_DB:
2038            # Remove db from tables
2039            expression = expression.transform(
2040                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2041            )
2042        this = self.sql(expression, "this")
2043        return f"RENAME TO {this}"
def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2045    def altertable_sql(self, expression: exp.AlterTable) -> str:
2046        actions = expression.args["actions"]
2047
2048        if isinstance(actions[0], exp.ColumnDef):
2049            actions = self.expressions(expression, key="actions", prefix="ADD COLUMN ")
2050        elif isinstance(actions[0], exp.Schema):
2051            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2052        elif isinstance(actions[0], exp.Delete):
2053            actions = self.expressions(expression, key="actions", flat=True)
2054        else:
2055            actions = self.expressions(expression, key="actions")
2056
2057        exists = " IF EXISTS" if expression.args.get("exists") else ""
2058        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
2060    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2061        expressions = self.expressions(expression)
2062        exists = " IF EXISTS " if expression.args.get("exists") else " "
2063        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
2065    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2066        this = self.sql(expression, "this")
2067        expression_ = self.sql(expression, "expression")
2068        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
2069
2070        enforced = expression.args.get("enforced")
2071        if enforced is not None:
2072            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
2073
2074        return f"{add_constraint} {expression_}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
2076    def distinct_sql(self, expression: exp.Distinct) -> str:
2077        this = self.expressions(expression, flat=True)
2078        this = f" {this}" if this else ""
2079
2080        on = self.sql(expression, "on")
2081        on = f" ON {on}" if on else ""
2082        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
2084    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2085        return f"{self.sql(expression, 'this')} IGNORE NULLS"
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
2087    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2088        return f"{self.sql(expression, 'this')} RESPECT NULLS"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
2090    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2091        return self.sql(
2092            exp.Cast(
2093                this=exp.Div(this=expression.this, expression=expression.expression),
2094                to=exp.DataType(this=exp.DataType.Type.INT),
2095            )
2096        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
2098    def dpipe_sql(self, expression: exp.DPipe) -> str:
2099        return self.binary(expression, "||")
def safedpipe_sql(self, expression: sqlglot.expressions.SafeDPipe) -> str:
2101    def safedpipe_sql(self, expression: exp.SafeDPipe) -> str:
2102        if self.STRICT_STRING_CONCAT:
2103            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2104        return self.dpipe_sql(expression)
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
2106    def div_sql(self, expression: exp.Div) -> str:
2107        return self.binary(expression, "/")
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
2109    def overlaps_sql(self, expression: exp.Overlaps) -> str:
2110        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
2112    def distance_sql(self, expression: exp.Distance) -> str:
2113        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
2115    def dot_sql(self, expression: exp.Dot) -> str:
2116        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
2118    def eq_sql(self, expression: exp.EQ) -> str:
2119        return self.binary(expression, "=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
2121    def escape_sql(self, expression: exp.Escape) -> str:
2122        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
2124    def glob_sql(self, expression: exp.Glob) -> str:
2125        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
2127    def gt_sql(self, expression: exp.GT) -> str:
2128        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
2130    def gte_sql(self, expression: exp.GTE) -> str:
2131        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
2133    def ilike_sql(self, expression: exp.ILike) -> str:
2134        return self.binary(expression, "ILIKE")
def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str:
2136    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
2137        return self.binary(expression, "ILIKE ANY")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
2139    def is_sql(self, expression: exp.Is) -> str:
2140        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
2141            return self.sql(
2142                expression.this if expression.expression.this else exp.not_(expression.this)
2143            )
2144        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
2146    def like_sql(self, expression: exp.Like) -> str:
2147        return self.binary(expression, "LIKE")
def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str:
2149    def likeany_sql(self, expression: exp.LikeAny) -> str:
2150        return self.binary(expression, "LIKE ANY")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
2152    def similarto_sql(self, expression: exp.SimilarTo) -> str:
2153        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
2155    def lt_sql(self, expression: exp.LT) -> str:
2156        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
2158    def lte_sql(self, expression: exp.LTE) -> str:
2159        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
2161    def mod_sql(self, expression: exp.Mod) -> str:
2162        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
2164    def mul_sql(self, expression: exp.Mul) -> str:
2165        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
2167    def neq_sql(self, expression: exp.NEQ) -> str:
2168        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
2170    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
2171        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
2173    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
2174        return self.binary(expression, "IS DISTINCT FROM")
def or_sql(self, expression: sqlglot.expressions.Or) -> str:
2176    def or_sql(self, expression: exp.Or) -> str:
2177        return self.connector_sql(expression, "OR")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
2179    def slice_sql(self, expression: exp.Slice) -> str:
2180        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
2182    def sub_sql(self, expression: exp.Sub) -> str:
2183        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
2185    def trycast_sql(self, expression: exp.TryCast) -> str:
2186        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
2188    def use_sql(self, expression: exp.Use) -> str:
2189        kind = self.sql(expression, "kind")
2190        kind = f" {kind}" if kind else ""
2191        this = self.sql(expression, "this")
2192        this = f" {this}" if this else ""
2193        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
2195    def binary(self, expression: exp.Binary, op: str) -> str:
2196        op = self.maybe_comment(op, comments=expression.comments)
2197        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
2199    def function_fallback_sql(self, expression: exp.Func) -> str:
2200        args = []
2201        for arg_value in expression.args.values():
2202            if isinstance(arg_value, list):
2203                for value in arg_value:
2204                    args.append(value)
2205            else:
2206                args.append(arg_value)
2207
2208        return self.func(expression.sql_name(), *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
2210    def func(
2211        self,
2212        name: str,
2213        *args: t.Optional[exp.Expression | str],
2214        prefix: str = "(",
2215        suffix: str = ")",
2216    ) -> str:
2217        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
2219    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
2220        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
2221        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
2222            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
2223        return ", ".join(arg_sqls)
def text_width(self, args: Iterable) -> int:
2225    def text_width(self, args: t.Iterable) -> int:
2226        return sum(len(arg) for arg in args)
def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]:
2228    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
2229        return format_time(
2230            self.sql(expression, "format"), self.INVERSE_TIME_MAPPING, self.INVERSE_TIME_TRIE
2231        )
def expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[List[str]] = None, flat: bool = False, indent: bool = True, sep: str = ', ', prefix: str = '') -> str:
2233    def expressions(
2234        self,
2235        expression: t.Optional[exp.Expression] = None,
2236        key: t.Optional[str] = None,
2237        sqls: t.Optional[t.List[str]] = None,
2238        flat: bool = False,
2239        indent: bool = True,
2240        sep: str = ", ",
2241        prefix: str = "",
2242    ) -> str:
2243        expressions = expression.args.get(key or "expressions") if expression else sqls
2244
2245        if not expressions:
2246            return ""
2247
2248        if flat:
2249            return sep.join(self.sql(e) for e in expressions)
2250
2251        num_sqls = len(expressions)
2252
2253        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
2254        pad = " " * self.pad
2255        stripped_sep = sep.strip()
2256
2257        result_sqls = []
2258        for i, e in enumerate(expressions):
2259            sql = self.sql(e, comment=False)
2260            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
2261
2262            if self.pretty:
2263                if self.leading_comma:
2264                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
2265                else:
2266                    result_sqls.append(
2267                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
2268                    )
2269            else:
2270                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
2271
2272        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
2273        return self.indent(result_sql, skip_first=False) if indent else result_sql
def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
2275    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
2276        flat = flat or isinstance(expression.parent, exp.Properties)
2277        expressions_sql = self.expressions(expression, flat=flat)
2278        if flat:
2279            return f"{op} {expressions_sql}"
2280        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
2282    def naked_property(self, expression: exp.Property) -> str:
2283        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
2284        if not property_name:
2285            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
2286        return f"{property_name} {self.sql(expression, 'this')}"
def set_operation(self, expression: sqlglot.expressions.Expression, op: str) -> str:
2288    def set_operation(self, expression: exp.Expression, op: str) -> str:
2289        this = self.sql(expression, "this")
2290        op = self.seg(op)
2291        return self.query_modifiers(
2292            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
2293        )
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
2295    def tag_sql(self, expression: exp.Tag) -> str:
2296        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
2298    def token_sql(self, token_type: TokenType) -> str:
2299        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
2301    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
2302        this = self.sql(expression, "this")
2303        expressions = self.no_identify(self.expressions, expression)
2304        expressions = (
2305            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
2306        )
2307        return f"{this}{expressions}"
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
2309    def joinhint_sql(self, expression: exp.JoinHint) -> str:
2310        this = self.sql(expression, "this")
2311        expressions = self.expressions(expression, flat=True)
2312        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
2314    def kwarg_sql(self, expression: exp.Kwarg) -> str:
2315        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
2317    def when_sql(self, expression: exp.When) -> str:
2318        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
2319        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
2320        condition = self.sql(expression, "condition")
2321        condition = f" AND {condition}" if condition else ""
2322
2323        then_expression = expression.args.get("then")
2324        if isinstance(then_expression, exp.Insert):
2325            then = f"INSERT {self.sql(then_expression, 'this')}"
2326            if "expression" in then_expression.args:
2327                then += f" VALUES {self.sql(then_expression, 'expression')}"
2328        elif isinstance(then_expression, exp.Update):
2329            if isinstance(then_expression.args.get("expressions"), exp.Star):
2330                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
2331            else:
2332                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
2333        else:
2334            then = self.sql(then_expression)
2335        return f"WHEN {matched}{source}{condition} THEN {then}"
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
2337    def merge_sql(self, expression: exp.Merge) -> str:
2338        this = self.sql(expression, "this")
2339        using = f"USING {self.sql(expression, 'using')}"
2340        on = f"ON {self.sql(expression, 'on')}"
2341        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
2343    def tochar_sql(self, expression: exp.ToChar) -> str:
2344        if expression.args.get("format"):
2345            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
2346
2347        return self.sql(exp.cast(expression.this, "text"))
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
2349    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
2350        this = self.sql(expression, "this")
2351        kind = self.sql(expression, "kind")
2352        settings_sql = self.expressions(expression, key="settings", sep=" ")
2353        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
2354        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
2356    def dictrange_sql(self, expression: exp.DictRange) -> str:
2357        this = self.sql(expression, "this")
2358        max = self.sql(expression, "max")
2359        min = self.sql(expression, "min")
2360        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
2362    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
2363        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
2365    def oncluster_sql(self, expression: exp.OnCluster) -> str:
2366        return ""
def cached_generator( cache: Optional[Dict[int, str]] = None) -> Callable[[sqlglot.expressions.Expression], str]:
2369def cached_generator(
2370    cache: t.Optional[t.Dict[int, str]] = None
2371) -> t.Callable[[exp.Expression], str]:
2372    """Returns a cached generator."""
2373    cache = {} if cache is None else cache
2374    generator = Generator(normalize=True, identify="safe")
2375    return lambda e: generator.generate(e, cache)

Returns a cached generator.