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
   9from sqlglot.time import format_time
  10from sqlglot.tokens import TokenType
  11
  12logger = logging.getLogger("sqlglot")
  13
  14
  15class Generator:
  16    """
  17    Generator interprets the given syntax tree and produces a SQL string as an output.
  18
  19    Args:
  20        time_mapping (dict): the dictionary of custom time mappings in which the key
  21            represents a python time format and the output the target time format
  22        time_trie (trie): a trie of the time_mapping keys
  23        pretty (bool): if set to True the returned string will be formatted. Default: False.
  24        quote_start (str): specifies which starting character to use to delimit quotes. Default: '.
  25        quote_end (str): specifies which ending character to use to delimit quotes. Default: '.
  26        identifier_start (str): specifies which starting character to use to delimit identifiers. Default: ".
  27        identifier_end (str): specifies which ending character to use to delimit identifiers. Default: ".
  28        identify (bool): if set to True all identifiers will be delimited by the corresponding
  29            character.
  30        normalize (bool): if set to True all identifiers will lower cased
  31        string_escape (str): specifies a string escape character. Default: '.
  32        identifier_escape (str): specifies an identifier escape character. Default: ".
  33        pad (int): determines padding in a formatted string. Default: 2.
  34        indent (int): determines the size of indentation in a formatted string. Default: 4.
  35        unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
  36        normalize_functions (str): normalize function names, "upper", "lower", or None
  37            Default: "upper"
  38        alias_post_tablesample (bool): if the table alias comes after tablesample
  39            Default: False
  40        unsupported_level (ErrorLevel): determines the generator's behavior when it encounters
  41            unsupported expressions. Default ErrorLevel.WARN.
  42        null_ordering (str): Indicates the default null ordering method to use if not explicitly set.
  43            Options are "nulls_are_small", "nulls_are_large", "nulls_are_last".
  44            Default: "nulls_are_small"
  45        max_unsupported (int): Maximum number of unsupported messages to include in a raised UnsupportedError.
  46            This is only relevant if unsupported_level is ErrorLevel.RAISE.
  47            Default: 3
  48        leading_comma (bool): if the the comma is leading or trailing in select statements
  49            Default: False
  50        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
  51            The default is on the smaller end because the length only represents a segment and not the true
  52            line length.
  53            Default: 80
  54        comments: Whether or not to preserve comments in the output SQL code.
  55            Default: True
  56    """
  57
  58    TRANSFORMS = {
  59        exp.DateAdd: lambda self, e: self.func(
  60            "DATE_ADD", e.this, e.expression, e.args.get("unit")
  61        ),
  62        exp.DateDiff: lambda self, e: self.func("DATEDIFF", e.this, e.expression),
  63        exp.TsOrDsAdd: lambda self, e: self.func(
  64            "TS_OR_DS_ADD", e.this, e.expression, e.args.get("unit")
  65        ),
  66        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
  67        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
  68        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
  69        exp.ExternalProperty: lambda self, e: "EXTERNAL",
  70        exp.LanguageProperty: lambda self, e: self.naked_property(e),
  71        exp.LocationProperty: lambda self, e: self.naked_property(e),
  72        exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
  73        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
  74        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
  75        exp.OnCommitProperty: lambda self, e: "ON COMMIT PRESERVE ROWS",
  76        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
  77        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
  78        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
  79        exp.TemporaryProperty: lambda self, e: f"{'GLOBAL ' if e.args.get('global_') else ''}TEMPORARY",
  80        exp.TransientProperty: lambda self, e: "TRANSIENT",
  81        exp.VolatilityProperty: lambda self, e: e.name,
  82        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
  83        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
  84        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
  85        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
  86        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
  87        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
  88        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
  89        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
  90        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
  91        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
  92        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
  93        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
  94        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
  95    }
  96
  97    # Whether or not null ordering is supported in order by
  98    NULL_ORDERING_SUPPORTED = True
  99
 100    # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 101    LOCKING_READS_SUPPORTED = False
 102
 103    # Always do union distinct or union all
 104    EXPLICIT_UNION = False
 105
 106    # Wrap derived values in parens, usually standard but spark doesn't support it
 107    WRAP_DERIVED_VALUES = True
 108
 109    # Whether or not create function uses an AS before the RETURN
 110    CREATE_FUNCTION_RETURN_AS = True
 111
 112    TYPE_MAPPING = {
 113        exp.DataType.Type.NCHAR: "CHAR",
 114        exp.DataType.Type.NVARCHAR: "VARCHAR",
 115        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 116        exp.DataType.Type.LONGTEXT: "TEXT",
 117        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 118        exp.DataType.Type.LONGBLOB: "BLOB",
 119        exp.DataType.Type.INET: "INET",
 120    }
 121
 122    STAR_MAPPING = {
 123        "except": "EXCEPT",
 124        "replace": "REPLACE",
 125    }
 126
 127    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 128
 129    STRUCT_DELIMITER = ("<", ">")
 130
 131    PARAMETER_TOKEN = "@"
 132
 133    PROPERTIES_LOCATION = {
 134        exp.AfterJournalProperty: exp.Properties.Location.POST_NAME,
 135        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 136        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 137        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 138        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 139        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 140        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 141        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 142        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 143        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 144        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 145        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 146        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 147        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 148        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 149        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 150        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 151        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 152        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 153        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 154        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 155        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 156        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 157        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 158        exp.LogProperty: exp.Properties.Location.POST_NAME,
 159        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 160        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 161        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 162        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 163        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 164        exp.Property: exp.Properties.Location.POST_WITH,
 165        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 166        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 167        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 168        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 169        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 170        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 171        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 172        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 173        exp.TableFormatProperty: exp.Properties.Location.POST_WITH,
 174        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 175        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 176        exp.VolatilityProperty: exp.Properties.Location.POST_SCHEMA,
 177        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 178        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 179    }
 180
 181    WITH_SEPARATED_COMMENTS = (exp.Select, exp.From, exp.Where, exp.Binary)
 182    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 183
 184    __slots__ = (
 185        "time_mapping",
 186        "time_trie",
 187        "pretty",
 188        "quote_start",
 189        "quote_end",
 190        "identifier_start",
 191        "identifier_end",
 192        "identify",
 193        "normalize",
 194        "string_escape",
 195        "identifier_escape",
 196        "pad",
 197        "index_offset",
 198        "unnest_column_only",
 199        "alias_post_tablesample",
 200        "normalize_functions",
 201        "unsupported_level",
 202        "unsupported_messages",
 203        "null_ordering",
 204        "max_unsupported",
 205        "_indent",
 206        "_escaped_quote_end",
 207        "_escaped_identifier_end",
 208        "_leading_comma",
 209        "_max_text_width",
 210        "_comments",
 211    )
 212
 213    def __init__(
 214        self,
 215        time_mapping=None,
 216        time_trie=None,
 217        pretty=None,
 218        quote_start=None,
 219        quote_end=None,
 220        identifier_start=None,
 221        identifier_end=None,
 222        identify=False,
 223        normalize=False,
 224        string_escape=None,
 225        identifier_escape=None,
 226        pad=2,
 227        indent=2,
 228        index_offset=0,
 229        unnest_column_only=False,
 230        alias_post_tablesample=False,
 231        normalize_functions="upper",
 232        unsupported_level=ErrorLevel.WARN,
 233        null_ordering=None,
 234        max_unsupported=3,
 235        leading_comma=False,
 236        max_text_width=80,
 237        comments=True,
 238    ):
 239        import sqlglot
 240
 241        self.time_mapping = time_mapping or {}
 242        self.time_trie = time_trie
 243        self.pretty = pretty if pretty is not None else sqlglot.pretty
 244        self.quote_start = quote_start or "'"
 245        self.quote_end = quote_end or "'"
 246        self.identifier_start = identifier_start or '"'
 247        self.identifier_end = identifier_end or '"'
 248        self.identify = identify
 249        self.normalize = normalize
 250        self.string_escape = string_escape or "'"
 251        self.identifier_escape = identifier_escape or '"'
 252        self.pad = pad
 253        self.index_offset = index_offset
 254        self.unnest_column_only = unnest_column_only
 255        self.alias_post_tablesample = alias_post_tablesample
 256        self.normalize_functions = normalize_functions
 257        self.unsupported_level = unsupported_level
 258        self.unsupported_messages = []
 259        self.max_unsupported = max_unsupported
 260        self.null_ordering = null_ordering
 261        self._indent = indent
 262        self._escaped_quote_end = self.string_escape + self.quote_end
 263        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
 264        self._leading_comma = leading_comma
 265        self._max_text_width = max_text_width
 266        self._comments = comments
 267
 268    def generate(self, expression: t.Optional[exp.Expression]) -> str:
 269        """
 270        Generates a SQL string by interpreting the given syntax tree.
 271
 272        Args
 273            expression: the syntax tree.
 274
 275        Returns
 276            the SQL string.
 277        """
 278        self.unsupported_messages = []
 279        sql = self.sql(expression).strip()
 280
 281        if self.unsupported_level == ErrorLevel.IGNORE:
 282            return sql
 283
 284        if self.unsupported_level == ErrorLevel.WARN:
 285            for msg in self.unsupported_messages:
 286                logger.warning(msg)
 287        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 288            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 289
 290        if self.pretty:
 291            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 292        return sql
 293
 294    def unsupported(self, message: str) -> None:
 295        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 296            raise UnsupportedError(message)
 297        self.unsupported_messages.append(message)
 298
 299    def sep(self, sep: str = " ") -> str:
 300        return f"{sep.strip()}\n" if self.pretty else sep
 301
 302    def seg(self, sql: str, sep: str = " ") -> str:
 303        return f"{self.sep(sep)}{sql}"
 304
 305    def pad_comment(self, comment: str) -> str:
 306        comment = " " + comment if comment[0].strip() else comment
 307        comment = comment + " " if comment[-1].strip() else comment
 308        return comment
 309
 310    def maybe_comment(self, sql: str, expression: exp.Expression) -> str:
 311        comments = expression.comments if self._comments else None
 312
 313        if not comments:
 314            return sql
 315
 316        sep = "\n" if self.pretty else " "
 317        comments_sql = sep.join(
 318            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 319        )
 320
 321        if not comments_sql:
 322            return sql
 323
 324        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 325            return f"{comments_sql}{self.sep()}{sql}"
 326
 327        return f"{sql} {comments_sql}"
 328
 329    def wrap(self, expression: exp.Expression | str) -> str:
 330        this_sql = self.indent(
 331            self.sql(expression)
 332            if isinstance(expression, (exp.Select, exp.Union))
 333            else self.sql(expression, "this"),
 334            level=1,
 335            pad=0,
 336        )
 337        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 338
 339    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 340        original = self.identify
 341        self.identify = False
 342        result = func(*args, **kwargs)
 343        self.identify = original
 344        return result
 345
 346    def normalize_func(self, name: str) -> str:
 347        if self.normalize_functions == "upper":
 348            return name.upper()
 349        if self.normalize_functions == "lower":
 350            return name.lower()
 351        return name
 352
 353    def indent(
 354        self,
 355        sql: str,
 356        level: int = 0,
 357        pad: t.Optional[int] = None,
 358        skip_first: bool = False,
 359        skip_last: bool = False,
 360    ) -> str:
 361        if not self.pretty:
 362            return sql
 363
 364        pad = self.pad if pad is None else pad
 365        lines = sql.split("\n")
 366
 367        return "\n".join(
 368            line
 369            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 370            else f"{' ' * (level * self._indent + pad)}{line}"
 371            for i, line in enumerate(lines)
 372        )
 373
 374    def sql(
 375        self,
 376        expression: t.Optional[str | exp.Expression],
 377        key: t.Optional[str] = None,
 378        comment: bool = True,
 379    ) -> str:
 380        if not expression:
 381            return ""
 382
 383        if isinstance(expression, str):
 384            return expression
 385
 386        if key:
 387            return self.sql(expression.args.get(key))
 388
 389        transform = self.TRANSFORMS.get(expression.__class__)
 390
 391        if callable(transform):
 392            sql = transform(self, expression)
 393        elif transform:
 394            sql = transform
 395        elif isinstance(expression, exp.Expression):
 396            exp_handler_name = f"{expression.key}_sql"
 397
 398            if hasattr(self, exp_handler_name):
 399                sql = getattr(self, exp_handler_name)(expression)
 400            elif isinstance(expression, exp.Func):
 401                sql = self.function_fallback_sql(expression)
 402            elif isinstance(expression, exp.Property):
 403                sql = self.property_sql(expression)
 404            else:
 405                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 406        else:
 407            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 408
 409        return self.maybe_comment(sql, expression) if self._comments and comment else sql
 410
 411    def uncache_sql(self, expression: exp.Uncache) -> str:
 412        table = self.sql(expression, "this")
 413        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 414        return f"UNCACHE TABLE{exists_sql} {table}"
 415
 416    def cache_sql(self, expression: exp.Cache) -> str:
 417        lazy = " LAZY" if expression.args.get("lazy") else ""
 418        table = self.sql(expression, "this")
 419        options = expression.args.get("options")
 420        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 421        sql = self.sql(expression, "expression")
 422        sql = f" AS{self.sep()}{sql}" if sql else ""
 423        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 424        return self.prepend_ctes(expression, sql)
 425
 426    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 427        if isinstance(expression.parent, exp.Cast):
 428            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 429        default = "DEFAULT " if expression.args.get("default") else ""
 430        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 431
 432    def column_sql(self, expression: exp.Column) -> str:
 433        return ".".join(
 434            self.sql(part)
 435            for part in (
 436                expression.args.get("catalog"),
 437                expression.args.get("db"),
 438                expression.args.get("table"),
 439                expression.args.get("this"),
 440            )
 441            if part
 442        )
 443
 444    def columndef_sql(self, expression: exp.ColumnDef) -> str:
 445        column = self.sql(expression, "this")
 446        kind = self.sql(expression, "kind")
 447        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 448        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 449        kind = f" {kind}" if kind else ""
 450        constraints = f" {constraints}" if constraints else ""
 451
 452        return f"{exists}{column}{kind}{constraints}"
 453
 454    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 455        this = self.sql(expression, "this")
 456        kind_sql = self.sql(expression, "kind")
 457        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 458
 459    def autoincrementcolumnconstraint_sql(self, _) -> str:
 460        return self.token_sql(TokenType.AUTO_INCREMENT)
 461
 462    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
 463        if isinstance(expression.this, list):
 464            this = self.wrap(self.expressions(expression, key="this", flat=True))
 465        else:
 466            this = self.sql(expression, "this")
 467
 468        return f"COMPRESS {this}"
 469
 470    def generatedasidentitycolumnconstraint_sql(
 471        self, expression: exp.GeneratedAsIdentityColumnConstraint
 472    ) -> str:
 473        this = ""
 474        if expression.this is not None:
 475            this = " ALWAYS " if expression.this else " BY DEFAULT "
 476        start = expression.args.get("start")
 477        start = f"START WITH {start}" if start else ""
 478        increment = expression.args.get("increment")
 479        increment = f" INCREMENT BY {increment}" if increment else ""
 480        minvalue = expression.args.get("minvalue")
 481        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 482        maxvalue = expression.args.get("maxvalue")
 483        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 484        cycle = expression.args.get("cycle")
 485        cycle_sql = ""
 486        if cycle is not None:
 487            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
 488            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
 489        sequence_opts = ""
 490        if start or increment or cycle_sql:
 491            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
 492            sequence_opts = f" ({sequence_opts.strip()})"
 493        return f"GENERATED{this}AS IDENTITY{sequence_opts}"
 494
 495    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
 496        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
 497
 498    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
 499        desc = expression.args.get("desc")
 500        if desc is not None:
 501            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
 502        return f"PRIMARY KEY"
 503
 504    def uniquecolumnconstraint_sql(self, _) -> str:
 505        return "UNIQUE"
 506
 507    def create_sql(self, expression: exp.Create) -> str:
 508        kind = self.sql(expression, "kind").upper()
 509        properties = expression.args.get("properties")
 510        properties_exp = expression.copy()
 511        properties_locs = self.locate_properties(properties) if properties else {}
 512        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
 513            exp.Properties.Location.POST_WITH
 514        ):
 515            properties_exp.set(
 516                "properties",
 517                exp.Properties(
 518                    expressions=[
 519                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
 520                        *properties_locs[exp.Properties.Location.POST_WITH],
 521                    ]
 522                ),
 523            )
 524        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
 525            this_name = self.sql(expression.this, "this")
 526            this_properties = self.properties(
 527                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
 528                wrapped=False,
 529            )
 530            this_schema = f"({self.expressions(expression.this)})"
 531            this = f"{this_name}, {this_properties} {this_schema}"
 532            properties_sql = ""
 533        else:
 534            this = self.sql(expression, "this")
 535            properties_sql = self.sql(properties_exp, "properties")
 536        begin = " BEGIN" if expression.args.get("begin") else ""
 537        expression_sql = self.sql(expression, "expression")
 538        if expression_sql:
 539            expression_sql = f"{begin}{self.sep()}{expression_sql}"
 540
 541            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
 542                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
 543                    postalias_props_sql = self.properties(
 544                        exp.Properties(
 545                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
 546                        ),
 547                        wrapped=False,
 548                    )
 549                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
 550                else:
 551                    expression_sql = f" AS{expression_sql}"
 552
 553        postindex_props_sql = ""
 554        if properties_locs.get(exp.Properties.Location.POST_INDEX):
 555            postindex_props_sql = self.properties(
 556                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
 557                wrapped=False,
 558                prefix=" ",
 559            )
 560
 561        indexes = expression.args.get("indexes")
 562        if indexes:
 563            indexes_sql: t.List[str] = []
 564            for index in indexes:
 565                ind_unique = " UNIQUE" if index.args.get("unique") else ""
 566                ind_primary = " PRIMARY" if index.args.get("primary") else ""
 567                ind_amp = " AMP" if index.args.get("amp") else ""
 568                ind_name = f" {index.name}" if index.name else ""
 569                ind_columns = (
 570                    f' ({self.expressions(index, key="columns", flat=True)})'
 571                    if index.args.get("columns")
 572                    else ""
 573                )
 574                ind_sql = f"{ind_unique}{ind_primary}{ind_amp} INDEX{ind_name}{ind_columns}"
 575
 576                if indexes_sql:
 577                    indexes_sql.append(ind_sql)
 578                else:
 579                    indexes_sql.append(
 580                        f"{ind_sql}{postindex_props_sql}"
 581                        if index.args.get("primary")
 582                        else f"{postindex_props_sql}{ind_sql}"
 583                    )
 584
 585            index_sql = "".join(indexes_sql)
 586        else:
 587            index_sql = postindex_props_sql
 588
 589        replace = " OR REPLACE" if expression.args.get("replace") else ""
 590        unique = " UNIQUE" if expression.args.get("unique") else ""
 591        volatile = " VOLATILE" if expression.args.get("volatile") else ""
 592
 593        postcreate_props_sql = ""
 594        if properties_locs.get(exp.Properties.Location.POST_CREATE):
 595            postcreate_props_sql = self.properties(
 596                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
 597                sep=" ",
 598                prefix=" ",
 599                wrapped=False,
 600            )
 601
 602        modifiers = "".join((replace, unique, volatile, postcreate_props_sql))
 603
 604        postexpression_props_sql = ""
 605        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
 606            postexpression_props_sql = self.properties(
 607                exp.Properties(
 608                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
 609                ),
 610                sep=" ",
 611                prefix=" ",
 612                wrapped=False,
 613            )
 614
 615        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
 616        no_schema_binding = (
 617            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
 618        )
 619
 620        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}"
 621        return self.prepend_ctes(expression, expression_sql)
 622
 623    def describe_sql(self, expression: exp.Describe) -> str:
 624        return f"DESCRIBE {self.sql(expression, 'this')}"
 625
 626    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
 627        with_ = self.sql(expression, "with")
 628        if with_:
 629            sql = f"{with_}{self.sep()}{sql}"
 630        return sql
 631
 632    def with_sql(self, expression: exp.With) -> str:
 633        sql = self.expressions(expression, flat=True)
 634        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
 635
 636        return f"WITH {recursive}{sql}"
 637
 638    def cte_sql(self, expression: exp.CTE) -> str:
 639        alias = self.sql(expression, "alias")
 640        return f"{alias} AS {self.wrap(expression)}"
 641
 642    def tablealias_sql(self, expression: exp.TableAlias) -> str:
 643        alias = self.sql(expression, "this")
 644        columns = self.expressions(expression, key="columns", flat=True)
 645        columns = f"({columns})" if columns else ""
 646        return f"{alias}{columns}"
 647
 648    def bitstring_sql(self, expression: exp.BitString) -> str:
 649        return self.sql(expression, "this")
 650
 651    def hexstring_sql(self, expression: exp.HexString) -> str:
 652        return self.sql(expression, "this")
 653
 654    def datatype_sql(self, expression: exp.DataType) -> str:
 655        type_value = expression.this
 656        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
 657        nested = ""
 658        interior = self.expressions(expression, flat=True)
 659        values = ""
 660        if interior:
 661            if expression.args.get("nested"):
 662                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
 663                if expression.args.get("values") is not None:
 664                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
 665                    values = (
 666                        f"{delimiters[0]}{self.expressions(expression, 'values')}{delimiters[1]}"
 667                    )
 668            else:
 669                nested = f"({interior})"
 670
 671        return f"{type_sql}{nested}{values}"
 672
 673    def directory_sql(self, expression: exp.Directory) -> str:
 674        local = "LOCAL " if expression.args.get("local") else ""
 675        row_format = self.sql(expression, "row_format")
 676        row_format = f" {row_format}" if row_format else ""
 677        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
 678
 679    def delete_sql(self, expression: exp.Delete) -> str:
 680        this = self.sql(expression, "this")
 681        this = f" FROM {this}" if this else ""
 682        using_sql = (
 683            f" USING {self.expressions(expression, 'using', sep=', USING ')}"
 684            if expression.args.get("using")
 685            else ""
 686        )
 687        where_sql = self.sql(expression, "where")
 688        sql = f"DELETE{this}{using_sql}{where_sql}"
 689        return self.prepend_ctes(expression, sql)
 690
 691    def drop_sql(self, expression: exp.Drop) -> str:
 692        this = self.sql(expression, "this")
 693        kind = expression.args["kind"]
 694        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
 695        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
 696        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
 697        cascade = " CASCADE" if expression.args.get("cascade") else ""
 698        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}"
 699
 700    def except_sql(self, expression: exp.Except) -> str:
 701        return self.prepend_ctes(
 702            expression,
 703            self.set_operation(expression, self.except_op(expression)),
 704        )
 705
 706    def except_op(self, expression: exp.Except) -> str:
 707        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
 708
 709    def fetch_sql(self, expression: exp.Fetch) -> str:
 710        direction = expression.args.get("direction")
 711        direction = f" {direction.upper()}" if direction else ""
 712        count = expression.args.get("count")
 713        count = f" {count}" if count else ""
 714        return f"{self.seg('FETCH')}{direction}{count} ROWS ONLY"
 715
 716    def filter_sql(self, expression: exp.Filter) -> str:
 717        this = self.sql(expression, "this")
 718        where = self.sql(expression, "expression")[1:]  # where has a leading space
 719        return f"{this} FILTER({where})"
 720
 721    def hint_sql(self, expression: exp.Hint) -> str:
 722        if self.sql(expression, "this"):
 723            self.unsupported("Hints are not supported")
 724        return ""
 725
 726    def index_sql(self, expression: exp.Index) -> str:
 727        this = self.sql(expression, "this")
 728        table = self.sql(expression, "table")
 729        columns = self.sql(expression, "columns")
 730        return f"{this} ON {table} {columns}"
 731
 732    def identifier_sql(self, expression: exp.Identifier) -> str:
 733        text = expression.name
 734        text = text.lower() if self.normalize else text
 735        text = text.replace(self.identifier_end, self._escaped_identifier_end)
 736        if expression.args.get("quoted") or self.identify:
 737            text = f"{self.identifier_start}{text}{self.identifier_end}"
 738        return text
 739
 740    def national_sql(self, expression: exp.National) -> str:
 741        return f"N{self.sql(expression, 'this')}"
 742
 743    def partition_sql(self, expression: exp.Partition) -> str:
 744        return f"PARTITION({self.expressions(expression)})"
 745
 746    def properties_sql(self, expression: exp.Properties) -> str:
 747        root_properties = []
 748        with_properties = []
 749
 750        for p in expression.expressions:
 751            p_loc = self.PROPERTIES_LOCATION[p.__class__]
 752            if p_loc == exp.Properties.Location.POST_WITH:
 753                with_properties.append(p)
 754            elif p_loc == exp.Properties.Location.POST_SCHEMA:
 755                root_properties.append(p)
 756
 757        return self.root_properties(
 758            exp.Properties(expressions=root_properties)
 759        ) + self.with_properties(exp.Properties(expressions=with_properties))
 760
 761    def root_properties(self, properties: exp.Properties) -> str:
 762        if properties.expressions:
 763            return self.sep() + self.expressions(properties, indent=False, sep=" ")
 764        return ""
 765
 766    def properties(
 767        self,
 768        properties: exp.Properties,
 769        prefix: str = "",
 770        sep: str = ", ",
 771        suffix: str = "",
 772        wrapped: bool = True,
 773    ) -> str:
 774        if properties.expressions:
 775            expressions = self.expressions(properties, sep=sep, indent=False)
 776            expressions = self.wrap(expressions) if wrapped else expressions
 777            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
 778        return ""
 779
 780    def with_properties(self, properties: exp.Properties) -> str:
 781        return self.properties(properties, prefix=self.seg("WITH"))
 782
 783    def locate_properties(
 784        self, properties: exp.Properties
 785    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
 786        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
 787            key: [] for key in exp.Properties.Location
 788        }
 789
 790        for p in properties.expressions:
 791            p_loc = self.PROPERTIES_LOCATION[p.__class__]
 792            if p_loc == exp.Properties.Location.POST_NAME:
 793                properties_locs[exp.Properties.Location.POST_NAME].append(p)
 794            elif p_loc == exp.Properties.Location.POST_INDEX:
 795                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
 796            elif p_loc == exp.Properties.Location.POST_SCHEMA:
 797                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
 798            elif p_loc == exp.Properties.Location.POST_WITH:
 799                properties_locs[exp.Properties.Location.POST_WITH].append(p)
 800            elif p_loc == exp.Properties.Location.POST_CREATE:
 801                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
 802            elif p_loc == exp.Properties.Location.POST_ALIAS:
 803                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
 804            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
 805                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
 806            elif p_loc == exp.Properties.Location.UNSUPPORTED:
 807                self.unsupported(f"Unsupported property {p.key}")
 808
 809        return properties_locs
 810
 811    def property_sql(self, expression: exp.Property) -> str:
 812        property_cls = expression.__class__
 813        if property_cls == exp.Property:
 814            return f"{expression.name}={self.sql(expression, 'value')}"
 815
 816        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
 817        if not property_name:
 818            self.unsupported(f"Unsupported property {expression.key}")
 819
 820        return f"{property_name}={self.sql(expression, 'this')}"
 821
 822    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
 823        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
 824        options = f" {options}" if options else ""
 825        return f"LIKE {self.sql(expression, 'this')}{options}"
 826
 827    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
 828        no = "NO " if expression.args.get("no") else ""
 829        protection = " PROTECTION" if expression.args.get("protection") else ""
 830        return f"{no}FALLBACK{protection}"
 831
 832    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
 833        no = "NO " if expression.args.get("no") else ""
 834        dual = "DUAL " if expression.args.get("dual") else ""
 835        before = "BEFORE " if expression.args.get("before") else ""
 836        return f"{no}{dual}{before}JOURNAL"
 837
 838    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
 839        freespace = self.sql(expression, "this")
 840        percent = " PERCENT" if expression.args.get("percent") else ""
 841        return f"FREESPACE={freespace}{percent}"
 842
 843    def afterjournalproperty_sql(self, expression: exp.AfterJournalProperty) -> str:
 844        no = "NO " if expression.args.get("no") else ""
 845        dual = "DUAL " if expression.args.get("dual") else ""
 846        local = ""
 847        if expression.args.get("local") is not None:
 848            local = "LOCAL " if expression.args.get("local") else "NOT LOCAL "
 849        return f"{no}{dual}{local}AFTER JOURNAL"
 850
 851    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
 852        if expression.args.get("default"):
 853            property = "DEFAULT"
 854        elif expression.args.get("on"):
 855            property = "ON"
 856        else:
 857            property = "OFF"
 858        return f"CHECKSUM={property}"
 859
 860    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
 861        if expression.args.get("no"):
 862            return "NO MERGEBLOCKRATIO"
 863        if expression.args.get("default"):
 864            return "DEFAULT MERGEBLOCKRATIO"
 865
 866        percent = " PERCENT" if expression.args.get("percent") else ""
 867        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
 868
 869    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
 870        default = expression.args.get("default")
 871        min = expression.args.get("min")
 872        if default is not None or min is not None:
 873            if default:
 874                property = "DEFAULT"
 875            elif min:
 876                property = "MINIMUM"
 877            else:
 878                property = "MAXIMUM"
 879            return f"{property} DATABLOCKSIZE"
 880        else:
 881            units = expression.args.get("units")
 882            units = f" {units}" if units else ""
 883            return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
 884
 885    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
 886        autotemp = expression.args.get("autotemp")
 887        always = expression.args.get("always")
 888        default = expression.args.get("default")
 889        manual = expression.args.get("manual")
 890        never = expression.args.get("never")
 891
 892        if autotemp is not None:
 893            property = f"AUTOTEMP({self.expressions(autotemp)})"
 894        elif always:
 895            property = "ALWAYS"
 896        elif default:
 897            property = "DEFAULT"
 898        elif manual:
 899            property = "MANUAL"
 900        elif never:
 901            property = "NEVER"
 902        return f"BLOCKCOMPRESSION={property}"
 903
 904    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
 905        no = expression.args.get("no")
 906        no = " NO" if no else ""
 907        concurrent = expression.args.get("concurrent")
 908        concurrent = " CONCURRENT" if concurrent else ""
 909
 910        for_ = ""
 911        if expression.args.get("for_all"):
 912            for_ = " FOR ALL"
 913        elif expression.args.get("for_insert"):
 914            for_ = " FOR INSERT"
 915        elif expression.args.get("for_none"):
 916            for_ = " FOR NONE"
 917        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
 918
 919    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
 920        kind = expression.args.get("kind")
 921        this: str = f" {this}" if expression.this else ""
 922        for_or_in = expression.args.get("for_or_in")
 923        lock_type = expression.args.get("lock_type")
 924        override = " OVERRIDE" if expression.args.get("override") else ""
 925        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
 926
 927    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
 928        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
 929        statistics = expression.args.get("statistics")
 930        statistics_sql = ""
 931        if statistics is not None:
 932            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
 933        return f"{data_sql}{statistics_sql}"
 934
 935    def insert_sql(self, expression: exp.Insert) -> str:
 936        overwrite = expression.args.get("overwrite")
 937
 938        if isinstance(expression.this, exp.Directory):
 939            this = "OVERWRITE " if overwrite else "INTO "
 940        else:
 941            this = "OVERWRITE TABLE " if overwrite else "INTO "
 942
 943        alternative = expression.args.get("alternative")
 944        alternative = f" OR {alternative} " if alternative else " "
 945        this = f"{this}{self.sql(expression, 'this')}"
 946
 947        exists = " IF EXISTS " if expression.args.get("exists") else " "
 948        partition_sql = (
 949            self.sql(expression, "partition") if expression.args.get("partition") else ""
 950        )
 951        expression_sql = self.sql(expression, "expression")
 952        sep = self.sep() if partition_sql else ""
 953        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}"
 954        return self.prepend_ctes(expression, sql)
 955
 956    def intersect_sql(self, expression: exp.Intersect) -> str:
 957        return self.prepend_ctes(
 958            expression,
 959            self.set_operation(expression, self.intersect_op(expression)),
 960        )
 961
 962    def intersect_op(self, expression: exp.Intersect) -> str:
 963        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
 964
 965    def introducer_sql(self, expression: exp.Introducer) -> str:
 966        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
 967
 968    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
 969        return expression.name.upper()
 970
 971    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
 972        fields = expression.args.get("fields")
 973        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
 974        escaped = expression.args.get("escaped")
 975        escaped = f" ESCAPED BY {escaped}" if escaped else ""
 976        items = expression.args.get("collection_items")
 977        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
 978        keys = expression.args.get("map_keys")
 979        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
 980        lines = expression.args.get("lines")
 981        lines = f" LINES TERMINATED BY {lines}" if lines else ""
 982        null = expression.args.get("null")
 983        null = f" NULL DEFINED AS {null}" if null else ""
 984        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
 985
 986    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
 987        table = ".".join(
 988            part
 989            for part in [
 990                self.sql(expression, "catalog"),
 991                self.sql(expression, "db"),
 992                self.sql(expression, "this"),
 993            ]
 994            if part
 995        )
 996
 997        alias = self.sql(expression, "alias")
 998        alias = f"{sep}{alias}" if alias else ""
 999        hints = self.expressions(expression, key="hints", sep=", ", flat=True)
1000        hints = f" WITH ({hints})" if hints else ""
1001        laterals = self.expressions(expression, key="laterals", sep="")
1002        joins = self.expressions(expression, key="joins", sep="")
1003        pivots = self.expressions(expression, key="pivots", sep="")
1004        system_time = expression.args.get("system_time")
1005        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
1006
1007        return f"{table}{system_time}{alias}{hints}{laterals}{joins}{pivots}"
1008
1009    def tablesample_sql(self, expression: exp.TableSample) -> str:
1010        if self.alias_post_tablesample and expression.this.alias:
1011            this = self.sql(expression.this, "this")
1012            alias = f" AS {self.sql(expression.this, 'alias')}"
1013        else:
1014            this = self.sql(expression, "this")
1015            alias = ""
1016        method = self.sql(expression, "method")
1017        method = f" {method.upper()} " if method else ""
1018        numerator = self.sql(expression, "bucket_numerator")
1019        denominator = self.sql(expression, "bucket_denominator")
1020        field = self.sql(expression, "bucket_field")
1021        field = f" ON {field}" if field else ""
1022        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1023        percent = self.sql(expression, "percent")
1024        percent = f"{percent} PERCENT" if percent else ""
1025        rows = self.sql(expression, "rows")
1026        rows = f"{rows} ROWS" if rows else ""
1027        size = self.sql(expression, "size")
1028        seed = self.sql(expression, "seed")
1029        seed = f" SEED ({seed})" if seed else ""
1030        return f"{this} TABLESAMPLE{method}({bucket}{percent}{rows}{size}){seed}{alias}"
1031
1032    def pivot_sql(self, expression: exp.Pivot) -> str:
1033        this = self.sql(expression, "this")
1034        alias = self.sql(expression, "alias")
1035        alias = f" AS {alias}" if alias else ""
1036        unpivot = expression.args.get("unpivot")
1037        direction = "UNPIVOT" if unpivot else "PIVOT"
1038        expressions = self.expressions(expression, key="expressions")
1039        field = self.sql(expression, "field")
1040        return f"{this} {direction}({expressions} FOR {field}){alias}"
1041
1042    def tuple_sql(self, expression: exp.Tuple) -> str:
1043        return f"({self.expressions(expression, flat=True)})"
1044
1045    def update_sql(self, expression: exp.Update) -> str:
1046        this = self.sql(expression, "this")
1047        set_sql = self.expressions(expression, flat=True)
1048        from_sql = self.sql(expression, "from")
1049        where_sql = self.sql(expression, "where")
1050        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}"
1051        return self.prepend_ctes(expression, sql)
1052
1053    def values_sql(self, expression: exp.Values) -> str:
1054        args = self.expressions(expression)
1055        alias = self.sql(expression, "alias")
1056        values = f"VALUES{self.seg('')}{args}"
1057        values = (
1058            f"({values})"
1059            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1060            else values
1061        )
1062        return f"{values} AS {alias}" if alias else values
1063
1064    def var_sql(self, expression: exp.Var) -> str:
1065        return self.sql(expression, "this")
1066
1067    def into_sql(self, expression: exp.Into) -> str:
1068        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1069        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1070        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
1071
1072    def from_sql(self, expression: exp.From) -> str:
1073        expressions = self.expressions(expression, flat=True)
1074        return f"{self.seg('FROM')} {expressions}"
1075
1076    def group_sql(self, expression: exp.Group) -> str:
1077        group_by = self.op_expressions("GROUP BY", expression)
1078        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1079        grouping_sets = (
1080            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1081        )
1082
1083        cube = expression.args.get("cube", [])
1084        if seq_get(cube, 0) is True:
1085            return f"{group_by}{self.seg('WITH CUBE')}"
1086        else:
1087            cube_sql = self.expressions(expression, key="cube", indent=False)
1088            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1089
1090        rollup = expression.args.get("rollup", [])
1091        if seq_get(rollup, 0) is True:
1092            return f"{group_by}{self.seg('WITH ROLLUP')}"
1093        else:
1094            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1095            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1096
1097        groupings = csv(grouping_sets, cube_sql, rollup_sql, sep=",")
1098
1099        if expression.args.get("expressions") and groupings:
1100            group_by = f"{group_by},"
1101
1102        return f"{group_by}{groupings}"
1103
1104    def having_sql(self, expression: exp.Having) -> str:
1105        this = self.indent(self.sql(expression, "this"))
1106        return f"{self.seg('HAVING')}{self.sep()}{this}"
1107
1108    def join_sql(self, expression: exp.Join) -> str:
1109        op_sql = self.seg(
1110            " ".join(
1111                op
1112                for op in (
1113                    "NATURAL" if expression.args.get("natural") else None,
1114                    expression.side,
1115                    expression.kind,
1116                    "JOIN",
1117                )
1118                if op
1119            )
1120        )
1121        on_sql = self.sql(expression, "on")
1122        using = expression.args.get("using")
1123
1124        if not on_sql and using:
1125            on_sql = csv(*(self.sql(column) for column in using))
1126
1127        if on_sql:
1128            on_sql = self.indent(on_sql, skip_first=True)
1129            space = self.seg(" " * self.pad) if self.pretty else " "
1130            if using:
1131                on_sql = f"{space}USING ({on_sql})"
1132            else:
1133                on_sql = f"{space}ON {on_sql}"
1134
1135        expression_sql = self.sql(expression, "expression")
1136        this_sql = self.sql(expression, "this")
1137        return f"{expression_sql}{op_sql} {this_sql}{on_sql}"
1138
1139    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1140        args = self.expressions(expression, flat=True)
1141        args = f"({args})" if len(args.split(",")) > 1 else args
1142        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
1143
1144    def lateral_sql(self, expression: exp.Lateral) -> str:
1145        this = self.sql(expression, "this")
1146
1147        if isinstance(expression.this, exp.Subquery):
1148            return f"LATERAL {this}"
1149
1150        if expression.args.get("view"):
1151            alias = expression.args["alias"]
1152            columns = self.expressions(alias, key="columns", flat=True)
1153            table = f" {alias.name}" if alias.name else ""
1154            columns = f" AS {columns}" if columns else ""
1155            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1156            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1157
1158        alias = self.sql(expression, "alias")
1159        alias = f" AS {alias}" if alias else ""
1160        return f"LATERAL {this}{alias}"
1161
1162    def limit_sql(self, expression: exp.Limit) -> str:
1163        this = self.sql(expression, "this")
1164        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"
1165
1166    def offset_sql(self, expression: exp.Offset) -> str:
1167        this = self.sql(expression, "this")
1168        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
1169
1170    def lock_sql(self, expression: exp.Lock) -> str:
1171        if self.LOCKING_READS_SUPPORTED:
1172            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
1173            return self.seg(f"FOR {lock_type}")
1174
1175        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1176        return ""
1177
1178    def literal_sql(self, expression: exp.Literal) -> str:
1179        text = expression.this or ""
1180        if expression.is_string:
1181            text = text.replace(self.quote_end, self._escaped_quote_end)
1182            if self.pretty:
1183                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1184            text = f"{self.quote_start}{text}{self.quote_end}"
1185        return text
1186
1187    def loaddata_sql(self, expression: exp.LoadData) -> str:
1188        local = " LOCAL" if expression.args.get("local") else ""
1189        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1190        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1191        this = f" INTO TABLE {self.sql(expression, 'this')}"
1192        partition = self.sql(expression, "partition")
1193        partition = f" {partition}" if partition else ""
1194        input_format = self.sql(expression, "input_format")
1195        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1196        serde = self.sql(expression, "serde")
1197        serde = f" SERDE {serde}" if serde else ""
1198        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
1199
1200    def null_sql(self, *_) -> str:
1201        return "NULL"
1202
1203    def boolean_sql(self, expression: exp.Boolean) -> str:
1204        return "TRUE" if expression.this else "FALSE"
1205
1206    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1207        this = self.sql(expression, "this")
1208        this = f"{this} " if this else this
1209        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
1210
1211    def cluster_sql(self, expression: exp.Cluster) -> str:
1212        return self.op_expressions("CLUSTER BY", expression)
1213
1214    def distribute_sql(self, expression: exp.Distribute) -> str:
1215        return self.op_expressions("DISTRIBUTE BY", expression)
1216
1217    def sort_sql(self, expression: exp.Sort) -> str:
1218        return self.op_expressions("SORT BY", expression)
1219
1220    def ordered_sql(self, expression: exp.Ordered) -> str:
1221        desc = expression.args.get("desc")
1222        asc = not desc
1223
1224        nulls_first = expression.args.get("nulls_first")
1225        nulls_last = not nulls_first
1226        nulls_are_large = self.null_ordering == "nulls_are_large"
1227        nulls_are_small = self.null_ordering == "nulls_are_small"
1228        nulls_are_last = self.null_ordering == "nulls_are_last"
1229
1230        sort_order = " DESC" if desc else ""
1231        nulls_sort_change = ""
1232        if nulls_first and (
1233            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
1234        ):
1235            nulls_sort_change = " NULLS FIRST"
1236        elif (
1237            nulls_last
1238            and ((asc and nulls_are_small) or (desc and nulls_are_large))
1239            and not nulls_are_last
1240        ):
1241            nulls_sort_change = " NULLS LAST"
1242
1243        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
1244            self.unsupported(
1245                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
1246            )
1247            nulls_sort_change = ""
1248
1249        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
1250
1251    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
1252        partition = self.partition_by_sql(expression)
1253        order = self.sql(expression, "order")
1254        measures = self.sql(expression, "measures")
1255        measures = self.seg(f"MEASURES {measures}") if measures else ""
1256        rows = self.sql(expression, "rows")
1257        rows = self.seg(rows) if rows else ""
1258        after = self.sql(expression, "after")
1259        after = self.seg(after) if after else ""
1260        pattern = self.sql(expression, "pattern")
1261        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
1262        define = self.sql(expression, "define")
1263        define = self.seg(f"DEFINE {define}") if define else ""
1264        body = "".join(
1265            (
1266                partition,
1267                order,
1268                measures,
1269                rows,
1270                after,
1271                pattern,
1272                define,
1273            )
1274        )
1275        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"
1276
1277    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
1278        return csv(
1279            *sqls,
1280            *[self.sql(sql) for sql in expression.args.get("joins") or []],
1281            self.sql(expression, "match"),
1282            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
1283            self.sql(expression, "where"),
1284            self.sql(expression, "group"),
1285            self.sql(expression, "having"),
1286            self.sql(expression, "qualify"),
1287            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
1288            if expression.args.get("windows")
1289            else "",
1290            self.sql(expression, "distribute"),
1291            self.sql(expression, "sort"),
1292            self.sql(expression, "cluster"),
1293            self.sql(expression, "order"),
1294            self.sql(expression, "limit"),
1295            self.sql(expression, "offset"),
1296            self.sql(expression, "lock"),
1297            sep="",
1298        )
1299
1300    def select_sql(self, expression: exp.Select) -> str:
1301        hint = self.sql(expression, "hint")
1302        distinct = self.sql(expression, "distinct")
1303        distinct = f" {distinct}" if distinct else ""
1304        expressions = self.expressions(expression)
1305        expressions = f"{self.sep()}{expressions}" if expressions else expressions
1306        sql = self.query_modifiers(
1307            expression,
1308            f"SELECT{hint}{distinct}{expressions}",
1309            self.sql(expression, "into", comment=False),
1310            self.sql(expression, "from", comment=False),
1311        )
1312        return self.prepend_ctes(expression, sql)
1313
1314    def schema_sql(self, expression: exp.Schema) -> str:
1315        this = self.sql(expression, "this")
1316        this = f"{this} " if this else ""
1317        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
1318        return f"{this}{sql}"
1319
1320    def star_sql(self, expression: exp.Star) -> str:
1321        except_ = self.expressions(expression, key="except", flat=True)
1322        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
1323        replace = self.expressions(expression, key="replace", flat=True)
1324        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
1325        return f"*{except_}{replace}"
1326
1327    def structkwarg_sql(self, expression: exp.StructKwarg) -> str:
1328        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1329
1330    def parameter_sql(self, expression: exp.Parameter) -> str:
1331        this = self.sql(expression, "this")
1332        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
1333        return f"{self.PARAMETER_TOKEN}{this}"
1334
1335    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
1336        this = self.sql(expression, "this")
1337        kind = expression.text("kind")
1338        if kind:
1339            kind = f"{kind}."
1340        return f"@@{kind}{this}"
1341
1342    def placeholder_sql(self, expression: exp.Placeholder) -> str:
1343        return f":{expression.name}" if expression.name else "?"
1344
1345    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
1346        alias = self.sql(expression, "alias")
1347        alias = f"{sep}{alias}" if alias else ""
1348
1349        sql = self.query_modifiers(
1350            expression,
1351            self.wrap(expression),
1352            self.expressions(expression, key="pivots", sep=" "),
1353            alias,
1354        )
1355
1356        return self.prepend_ctes(expression, sql)
1357
1358    def qualify_sql(self, expression: exp.Qualify) -> str:
1359        this = self.indent(self.sql(expression, "this"))
1360        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
1361
1362    def union_sql(self, expression: exp.Union) -> str:
1363        return self.prepend_ctes(
1364            expression,
1365            self.set_operation(expression, self.union_op(expression)),
1366        )
1367
1368    def union_op(self, expression: exp.Union) -> str:
1369        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
1370        kind = kind if expression.args.get("distinct") else " ALL"
1371        return f"UNION{kind}"
1372
1373    def unnest_sql(self, expression: exp.Unnest) -> str:
1374        args = self.expressions(expression, flat=True)
1375        alias = expression.args.get("alias")
1376        if alias and self.unnest_column_only:
1377            columns = alias.columns
1378            alias = self.sql(columns[0]) if columns else ""
1379        else:
1380            alias = self.sql(expression, "alias")
1381        alias = f" AS {alias}" if alias else alias
1382        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
1383        offset = expression.args.get("offset")
1384        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
1385        return f"UNNEST({args}){ordinality}{alias}{offset}"
1386
1387    def where_sql(self, expression: exp.Where) -> str:
1388        this = self.indent(self.sql(expression, "this"))
1389        return f"{self.seg('WHERE')}{self.sep()}{this}"
1390
1391    def window_sql(self, expression: exp.Window) -> str:
1392        this = self.sql(expression, "this")
1393
1394        partition = self.partition_by_sql(expression)
1395
1396        order = expression.args.get("order")
1397        order_sql = self.order_sql(order, flat=True) if order else ""
1398
1399        partition_sql = partition + " " if partition and order else partition
1400
1401        spec = expression.args.get("spec")
1402        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
1403
1404        alias = self.sql(expression, "alias")
1405        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
1406
1407        if not partition and not order and not spec and alias:
1408            return f"{this} {alias}"
1409
1410        window_args = alias + partition_sql + order_sql + spec_sql
1411
1412        return f"{this} ({window_args.strip()})"
1413
1414    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
1415        partition = self.expressions(expression, key="partition_by", flat=True)
1416        return f"PARTITION BY {partition}" if partition else ""
1417
1418    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
1419        kind = self.sql(expression, "kind")
1420        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
1421        end = (
1422            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
1423            or "CURRENT ROW"
1424        )
1425        return f"{kind} BETWEEN {start} AND {end}"
1426
1427    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
1428        this = self.sql(expression, "this")
1429        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
1430        return f"{this} WITHIN GROUP ({expression_sql})"
1431
1432    def between_sql(self, expression: exp.Between) -> str:
1433        this = self.sql(expression, "this")
1434        low = self.sql(expression, "low")
1435        high = self.sql(expression, "high")
1436        return f"{this} BETWEEN {low} AND {high}"
1437
1438    def bracket_sql(self, expression: exp.Bracket) -> str:
1439        expressions = apply_index_offset(expression.expressions, self.index_offset)
1440        expressions_sql = ", ".join(self.sql(e) for e in expressions)
1441
1442        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
1443
1444    def all_sql(self, expression: exp.All) -> str:
1445        return f"ALL {self.wrap(expression)}"
1446
1447    def any_sql(self, expression: exp.Any) -> str:
1448        this = self.sql(expression, "this")
1449        if isinstance(expression.this, exp.Subqueryable):
1450            this = self.wrap(this)
1451        return f"ANY {this}"
1452
1453    def exists_sql(self, expression: exp.Exists) -> str:
1454        return f"EXISTS{self.wrap(expression)}"
1455
1456    def case_sql(self, expression: exp.Case) -> str:
1457        this = self.sql(expression, "this")
1458        statements = [f"CASE {this}" if this else "CASE"]
1459
1460        for e in expression.args["ifs"]:
1461            statements.append(f"WHEN {self.sql(e, 'this')}")
1462            statements.append(f"THEN {self.sql(e, 'true')}")
1463
1464        default = self.sql(expression, "default")
1465
1466        if default:
1467            statements.append(f"ELSE {default}")
1468
1469        statements.append("END")
1470
1471        if self.pretty and self.text_width(statements) > self._max_text_width:
1472            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
1473
1474        return " ".join(statements)
1475
1476    def constraint_sql(self, expression: exp.Constraint) -> str:
1477        this = self.sql(expression, "this")
1478        expressions = self.expressions(expression, flat=True)
1479        return f"CONSTRAINT {this} {expressions}"
1480
1481    def extract_sql(self, expression: exp.Extract) -> str:
1482        this = self.sql(expression, "this")
1483        expression_sql = self.sql(expression, "expression")
1484        return f"EXTRACT({this} FROM {expression_sql})"
1485
1486    def trim_sql(self, expression: exp.Trim) -> str:
1487        trim_type = self.sql(expression, "position")
1488
1489        if trim_type == "LEADING":
1490            return self.func("LTRIM", expression.this)
1491        elif trim_type == "TRAILING":
1492            return self.func("RTRIM", expression.this)
1493        else:
1494            return self.func("TRIM", expression.this, expression.expression)
1495
1496    def concat_sql(self, expression: exp.Concat) -> str:
1497        if len(expression.expressions) == 1:
1498            return self.sql(expression.expressions[0])
1499        return self.function_fallback_sql(expression)
1500
1501    def check_sql(self, expression: exp.Check) -> str:
1502        this = self.sql(expression, key="this")
1503        return f"CHECK ({this})"
1504
1505    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
1506        expressions = self.expressions(expression, flat=True)
1507        reference = self.sql(expression, "reference")
1508        reference = f" {reference}" if reference else ""
1509        delete = self.sql(expression, "delete")
1510        delete = f" ON DELETE {delete}" if delete else ""
1511        update = self.sql(expression, "update")
1512        update = f" ON UPDATE {update}" if update else ""
1513        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
1514
1515    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
1516        expressions = self.expressions(expression, flat=True)
1517        options = self.expressions(expression, "options", flat=True, sep=" ")
1518        options = f" {options}" if options else ""
1519        return f"PRIMARY KEY ({expressions}){options}"
1520
1521    def unique_sql(self, expression: exp.Unique) -> str:
1522        columns = self.expressions(expression, key="expressions")
1523        return f"UNIQUE ({columns})"
1524
1525    def if_sql(self, expression: exp.If) -> str:
1526        return self.case_sql(
1527            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
1528        )
1529
1530    def in_sql(self, expression: exp.In) -> str:
1531        query = expression.args.get("query")
1532        unnest = expression.args.get("unnest")
1533        field = expression.args.get("field")
1534        is_global = " GLOBAL" if expression.args.get("is_global") else ""
1535
1536        if query:
1537            in_sql = self.wrap(query)
1538        elif unnest:
1539            in_sql = self.in_unnest_op(unnest)
1540        elif field:
1541            in_sql = self.sql(field)
1542        else:
1543            in_sql = f"({self.expressions(expression, flat=True)})"
1544
1545        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
1546
1547    def in_unnest_op(self, unnest: exp.Unnest) -> str:
1548        return f"(SELECT {self.sql(unnest)})"
1549
1550    def interval_sql(self, expression: exp.Interval) -> str:
1551        this = expression.args.get("this")
1552        if this:
1553            this = (
1554                f" {this}"
1555                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
1556                else f" ({this})"
1557            )
1558        else:
1559            this = ""
1560        unit = expression.args.get("unit")
1561        unit = f" {unit}" if unit else ""
1562        return f"INTERVAL{this}{unit}"
1563
1564    def return_sql(self, expression: exp.Return) -> str:
1565        return f"RETURN {self.sql(expression, 'this')}"
1566
1567    def reference_sql(self, expression: exp.Reference) -> str:
1568        this = self.sql(expression, "this")
1569        expressions = self.expressions(expression, flat=True)
1570        expressions = f"({expressions})" if expressions else ""
1571        options = self.expressions(expression, "options", flat=True, sep=" ")
1572        options = f" {options}" if options else ""
1573        return f"REFERENCES {this}{expressions}{options}"
1574
1575    def anonymous_sql(self, expression: exp.Anonymous) -> str:
1576        return self.func(expression.name, *expression.expressions)
1577
1578    def paren_sql(self, expression: exp.Paren) -> str:
1579        if isinstance(expression.unnest(), exp.Select):
1580            sql = self.wrap(expression)
1581        else:
1582            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
1583            sql = f"({sql}{self.seg(')', sep='')}"
1584
1585        return self.prepend_ctes(expression, sql)
1586
1587    def neg_sql(self, expression: exp.Neg) -> str:
1588        # This makes sure we don't convert "- - 5" to "--5", which is a comment
1589        this_sql = self.sql(expression, "this")
1590        sep = " " if this_sql[0] == "-" else ""
1591        return f"-{sep}{this_sql}"
1592
1593    def not_sql(self, expression: exp.Not) -> str:
1594        return f"NOT {self.sql(expression, 'this')}"
1595
1596    def alias_sql(self, expression: exp.Alias) -> str:
1597        to_sql = self.sql(expression, "alias")
1598        to_sql = f" AS {to_sql}" if to_sql else ""
1599        return f"{self.sql(expression, 'this')}{to_sql}"
1600
1601    def aliases_sql(self, expression: exp.Aliases) -> str:
1602        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
1603
1604    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
1605        this = self.sql(expression, "this")
1606        zone = self.sql(expression, "zone")
1607        return f"{this} AT TIME ZONE {zone}"
1608
1609    def add_sql(self, expression: exp.Add) -> str:
1610        return self.binary(expression, "+")
1611
1612    def and_sql(self, expression: exp.And) -> str:
1613        return self.connector_sql(expression, "AND")
1614
1615    def connector_sql(self, expression: exp.Connector, op: str) -> str:
1616        if not self.pretty:
1617            return self.binary(expression, op)
1618
1619        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
1620        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
1621        return f"{sep}{op} ".join(sqls)
1622
1623    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
1624        return self.binary(expression, "&")
1625
1626    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
1627        return self.binary(expression, "<<")
1628
1629    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
1630        return f"~{self.sql(expression, 'this')}"
1631
1632    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
1633        return self.binary(expression, "|")
1634
1635    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
1636        return self.binary(expression, ">>")
1637
1638    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
1639        return self.binary(expression, "^")
1640
1641    def cast_sql(self, expression: exp.Cast) -> str:
1642        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
1643
1644    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
1645        zone = self.sql(expression, "this")
1646        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
1647
1648    def collate_sql(self, expression: exp.Collate) -> str:
1649        return self.binary(expression, "COLLATE")
1650
1651    def command_sql(self, expression: exp.Command) -> str:
1652        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
1653
1654    def comment_sql(self, expression: exp.Comment) -> str:
1655        this = self.sql(expression, "this")
1656        kind = expression.args["kind"]
1657        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1658        expression_sql = self.sql(expression, "expression")
1659        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
1660
1661    def transaction_sql(self, *_) -> str:
1662        return "BEGIN"
1663
1664    def commit_sql(self, expression: exp.Commit) -> str:
1665        chain = expression.args.get("chain")
1666        if chain is not None:
1667            chain = " AND CHAIN" if chain else " AND NO CHAIN"
1668
1669        return f"COMMIT{chain or ''}"
1670
1671    def rollback_sql(self, expression: exp.Rollback) -> str:
1672        savepoint = expression.args.get("savepoint")
1673        savepoint = f" TO {savepoint}" if savepoint else ""
1674        return f"ROLLBACK{savepoint}"
1675
1676    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
1677        this = self.sql(expression, "this")
1678
1679        dtype = self.sql(expression, "dtype")
1680        if dtype:
1681            collate = self.sql(expression, "collate")
1682            collate = f" COLLATE {collate}" if collate else ""
1683            using = self.sql(expression, "using")
1684            using = f" USING {using}" if using else ""
1685            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
1686
1687        default = self.sql(expression, "default")
1688        if default:
1689            return f"ALTER COLUMN {this} SET DEFAULT {default}"
1690
1691        if not expression.args.get("drop"):
1692            self.unsupported("Unsupported ALTER COLUMN syntax")
1693
1694        return f"ALTER COLUMN {this} DROP DEFAULT"
1695
1696    def renametable_sql(self, expression: exp.RenameTable) -> str:
1697        this = self.sql(expression, "this")
1698        return f"RENAME TO {this}"
1699
1700    def altertable_sql(self, expression: exp.AlterTable) -> str:
1701        actions = expression.args["actions"]
1702
1703        if isinstance(actions[0], exp.ColumnDef):
1704            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
1705        elif isinstance(actions[0], exp.Schema):
1706            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
1707        elif isinstance(actions[0], exp.Delete):
1708            actions = self.expressions(expression, "actions", flat=True)
1709        else:
1710            actions = self.expressions(expression, "actions")
1711
1712        exists = " IF EXISTS" if expression.args.get("exists") else ""
1713        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
1714
1715    def droppartition_sql(self, expression: exp.DropPartition) -> str:
1716        expressions = self.expressions(expression)
1717        exists = " IF EXISTS " if expression.args.get("exists") else " "
1718        return f"DROP{exists}{expressions}"
1719
1720    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
1721        this = self.sql(expression, "this")
1722        expression_ = self.sql(expression, "expression")
1723        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
1724
1725        enforced = expression.args.get("enforced")
1726        if enforced is not None:
1727            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
1728
1729        return f"{add_constraint} {expression_}"
1730
1731    def distinct_sql(self, expression: exp.Distinct) -> str:
1732        this = self.expressions(expression, flat=True)
1733        this = f" {this}" if this else ""
1734
1735        on = self.sql(expression, "on")
1736        on = f" ON {on}" if on else ""
1737        return f"DISTINCT{this}{on}"
1738
1739    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
1740        return f"{self.sql(expression, 'this')} IGNORE NULLS"
1741
1742    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
1743        return f"{self.sql(expression, 'this')} RESPECT NULLS"
1744
1745    def intdiv_sql(self, expression: exp.IntDiv) -> str:
1746        return self.sql(
1747            exp.Cast(
1748                this=exp.Div(this=expression.this, expression=expression.expression),
1749                to=exp.DataType(this=exp.DataType.Type.INT),
1750            )
1751        )
1752
1753    def dpipe_sql(self, expression: exp.DPipe) -> str:
1754        return self.binary(expression, "||")
1755
1756    def div_sql(self, expression: exp.Div) -> str:
1757        return self.binary(expression, "/")
1758
1759    def overlaps_sql(self, expression: exp.Overlaps) -> str:
1760        return self.binary(expression, "OVERLAPS")
1761
1762    def distance_sql(self, expression: exp.Distance) -> str:
1763        return self.binary(expression, "<->")
1764
1765    def dot_sql(self, expression: exp.Dot) -> str:
1766        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
1767
1768    def eq_sql(self, expression: exp.EQ) -> str:
1769        return self.binary(expression, "=")
1770
1771    def escape_sql(self, expression: exp.Escape) -> str:
1772        return self.binary(expression, "ESCAPE")
1773
1774    def glob_sql(self, expression: exp.Glob) -> str:
1775        return self.binary(expression, "GLOB")
1776
1777    def gt_sql(self, expression: exp.GT) -> str:
1778        return self.binary(expression, ">")
1779
1780    def gte_sql(self, expression: exp.GTE) -> str:
1781        return self.binary(expression, ">=")
1782
1783    def ilike_sql(self, expression: exp.ILike) -> str:
1784        return self.binary(expression, "ILIKE")
1785
1786    def is_sql(self, expression: exp.Is) -> str:
1787        return self.binary(expression, "IS")
1788
1789    def like_sql(self, expression: exp.Like) -> str:
1790        return self.binary(expression, "LIKE")
1791
1792    def similarto_sql(self, expression: exp.SimilarTo) -> str:
1793        return self.binary(expression, "SIMILAR TO")
1794
1795    def lt_sql(self, expression: exp.LT) -> str:
1796        return self.binary(expression, "<")
1797
1798    def lte_sql(self, expression: exp.LTE) -> str:
1799        return self.binary(expression, "<=")
1800
1801    def mod_sql(self, expression: exp.Mod) -> str:
1802        return self.binary(expression, "%")
1803
1804    def mul_sql(self, expression: exp.Mul) -> str:
1805        return self.binary(expression, "*")
1806
1807    def neq_sql(self, expression: exp.NEQ) -> str:
1808        return self.binary(expression, "<>")
1809
1810    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
1811        return self.binary(expression, "IS NOT DISTINCT FROM")
1812
1813    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
1814        return self.binary(expression, "IS DISTINCT FROM")
1815
1816    def or_sql(self, expression: exp.Or) -> str:
1817        return self.connector_sql(expression, "OR")
1818
1819    def slice_sql(self, expression: exp.Slice) -> str:
1820        return self.binary(expression, ":")
1821
1822    def sub_sql(self, expression: exp.Sub) -> str:
1823        return self.binary(expression, "-")
1824
1825    def trycast_sql(self, expression: exp.TryCast) -> str:
1826        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
1827
1828    def use_sql(self, expression: exp.Use) -> str:
1829        kind = self.sql(expression, "kind")
1830        kind = f" {kind}" if kind else ""
1831        this = self.sql(expression, "this")
1832        this = f" {this}" if this else ""
1833        return f"USE{kind}{this}"
1834
1835    def binary(self, expression: exp.Binary, op: str) -> str:
1836        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
1837
1838    def function_fallback_sql(self, expression: exp.Func) -> str:
1839        args = []
1840        for arg_value in expression.args.values():
1841            if isinstance(arg_value, list):
1842                for value in arg_value:
1843                    args.append(value)
1844            else:
1845                args.append(arg_value)
1846
1847        return self.func(expression.sql_name(), *args)
1848
1849    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
1850        return f"{self.normalize_func(name)}({self.format_args(*args)})"
1851
1852    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
1853        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
1854        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
1855            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
1856        return ", ".join(arg_sqls)
1857
1858    def text_width(self, args: t.Iterable) -> int:
1859        return sum(len(arg) for arg in args)
1860
1861    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
1862        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)
1863
1864    def expressions(
1865        self,
1866        expression: exp.Expression,
1867        key: t.Optional[str] = None,
1868        flat: bool = False,
1869        indent: bool = True,
1870        sep: str = ", ",
1871        prefix: str = "",
1872    ) -> str:
1873        expressions = expression.args.get(key or "expressions")
1874
1875        if not expressions:
1876            return ""
1877
1878        if flat:
1879            return sep.join(self.sql(e) for e in expressions)
1880
1881        num_sqls = len(expressions)
1882
1883        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
1884        pad = " " * self.pad
1885        stripped_sep = sep.strip()
1886
1887        result_sqls = []
1888        for i, e in enumerate(expressions):
1889            sql = self.sql(e, comment=False)
1890            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
1891
1892            if self.pretty:
1893                if self._leading_comma:
1894                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
1895                else:
1896                    result_sqls.append(
1897                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
1898                    )
1899            else:
1900                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
1901
1902        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
1903        return self.indent(result_sql, skip_first=False) if indent else result_sql
1904
1905    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
1906        flat = flat or isinstance(expression.parent, exp.Properties)
1907        expressions_sql = self.expressions(expression, flat=flat)
1908        if flat:
1909            return f"{op} {expressions_sql}"
1910        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
1911
1912    def naked_property(self, expression: exp.Property) -> str:
1913        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
1914        if not property_name:
1915            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
1916        return f"{property_name} {self.sql(expression, 'this')}"
1917
1918    def set_operation(self, expression: exp.Expression, op: str) -> str:
1919        this = self.sql(expression, "this")
1920        op = self.seg(op)
1921        return self.query_modifiers(
1922            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
1923        )
1924
1925    def tag_sql(self, expression: exp.Tag) -> str:
1926        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
1927
1928    def token_sql(self, token_type: TokenType) -> str:
1929        return self.TOKEN_MAPPING.get(token_type, token_type.name)
1930
1931    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
1932        this = self.sql(expression, "this")
1933        expressions = self.no_identify(self.expressions, expression)
1934        expressions = (
1935            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
1936        )
1937        return f"{this}{expressions}"
1938
1939    def joinhint_sql(self, expression: exp.JoinHint) -> str:
1940        this = self.sql(expression, "this")
1941        expressions = self.expressions(expression, flat=True)
1942        return f"{this}({expressions})"
1943
1944    def kwarg_sql(self, expression: exp.Kwarg) -> str:
1945        return self.binary(expression, "=>")
1946
1947    def when_sql(self, expression: exp.When) -> str:
1948        this = self.sql(expression, "this")
1949        then_expression = expression.args.get("then")
1950        if isinstance(then_expression, exp.Insert):
1951            then = f"INSERT {self.sql(then_expression, 'this')}"
1952            if "expression" in then_expression.args:
1953                then += f" VALUES {self.sql(then_expression, 'expression')}"
1954        elif isinstance(then_expression, exp.Update):
1955            if isinstance(then_expression.args.get("expressions"), exp.Star):
1956                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
1957            else:
1958                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
1959        else:
1960            then = self.sql(then_expression)
1961        return f"WHEN {this} THEN {then}"
1962
1963    def merge_sql(self, expression: exp.Merge) -> str:
1964        this = self.sql(expression, "this")
1965        using = f"USING {self.sql(expression, 'using')}"
1966        on = f"ON {self.sql(expression, 'on')}"
1967        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"
class Generator:
  16class Generator:
  17    """
  18    Generator interprets the given syntax tree and produces a SQL string as an output.
  19
  20    Args:
  21        time_mapping (dict): the dictionary of custom time mappings in which the key
  22            represents a python time format and the output the target time format
  23        time_trie (trie): a trie of the time_mapping keys
  24        pretty (bool): if set to True the returned string will be formatted. Default: False.
  25        quote_start (str): specifies which starting character to use to delimit quotes. Default: '.
  26        quote_end (str): specifies which ending character to use to delimit quotes. Default: '.
  27        identifier_start (str): specifies which starting character to use to delimit identifiers. Default: ".
  28        identifier_end (str): specifies which ending character to use to delimit identifiers. Default: ".
  29        identify (bool): if set to True all identifiers will be delimited by the corresponding
  30            character.
  31        normalize (bool): if set to True all identifiers will lower cased
  32        string_escape (str): specifies a string escape character. Default: '.
  33        identifier_escape (str): specifies an identifier escape character. Default: ".
  34        pad (int): determines padding in a formatted string. Default: 2.
  35        indent (int): determines the size of indentation in a formatted string. Default: 4.
  36        unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
  37        normalize_functions (str): normalize function names, "upper", "lower", or None
  38            Default: "upper"
  39        alias_post_tablesample (bool): if the table alias comes after tablesample
  40            Default: False
  41        unsupported_level (ErrorLevel): determines the generator's behavior when it encounters
  42            unsupported expressions. Default ErrorLevel.WARN.
  43        null_ordering (str): Indicates the default null ordering method to use if not explicitly set.
  44            Options are "nulls_are_small", "nulls_are_large", "nulls_are_last".
  45            Default: "nulls_are_small"
  46        max_unsupported (int): Maximum number of unsupported messages to include in a raised UnsupportedError.
  47            This is only relevant if unsupported_level is ErrorLevel.RAISE.
  48            Default: 3
  49        leading_comma (bool): if the the comma is leading or trailing in select statements
  50            Default: False
  51        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
  52            The default is on the smaller end because the length only represents a segment and not the true
  53            line length.
  54            Default: 80
  55        comments: Whether or not to preserve comments in the output SQL code.
  56            Default: True
  57    """
  58
  59    TRANSFORMS = {
  60        exp.DateAdd: lambda self, e: self.func(
  61            "DATE_ADD", e.this, e.expression, e.args.get("unit")
  62        ),
  63        exp.DateDiff: lambda self, e: self.func("DATEDIFF", e.this, e.expression),
  64        exp.TsOrDsAdd: lambda self, e: self.func(
  65            "TS_OR_DS_ADD", e.this, e.expression, e.args.get("unit")
  66        ),
  67        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
  68        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
  69        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
  70        exp.ExternalProperty: lambda self, e: "EXTERNAL",
  71        exp.LanguageProperty: lambda self, e: self.naked_property(e),
  72        exp.LocationProperty: lambda self, e: self.naked_property(e),
  73        exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
  74        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
  75        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
  76        exp.OnCommitProperty: lambda self, e: "ON COMMIT PRESERVE ROWS",
  77        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
  78        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
  79        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
  80        exp.TemporaryProperty: lambda self, e: f"{'GLOBAL ' if e.args.get('global_') else ''}TEMPORARY",
  81        exp.TransientProperty: lambda self, e: "TRANSIENT",
  82        exp.VolatilityProperty: lambda self, e: e.name,
  83        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
  84        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
  85        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
  86        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
  87        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
  88        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
  89        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
  90        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
  91        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
  92        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
  93        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
  94        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
  95        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
  96    }
  97
  98    # Whether or not null ordering is supported in order by
  99    NULL_ORDERING_SUPPORTED = True
 100
 101    # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
 102    LOCKING_READS_SUPPORTED = False
 103
 104    # Always do union distinct or union all
 105    EXPLICIT_UNION = False
 106
 107    # Wrap derived values in parens, usually standard but spark doesn't support it
 108    WRAP_DERIVED_VALUES = True
 109
 110    # Whether or not create function uses an AS before the RETURN
 111    CREATE_FUNCTION_RETURN_AS = True
 112
 113    TYPE_MAPPING = {
 114        exp.DataType.Type.NCHAR: "CHAR",
 115        exp.DataType.Type.NVARCHAR: "VARCHAR",
 116        exp.DataType.Type.MEDIUMTEXT: "TEXT",
 117        exp.DataType.Type.LONGTEXT: "TEXT",
 118        exp.DataType.Type.MEDIUMBLOB: "BLOB",
 119        exp.DataType.Type.LONGBLOB: "BLOB",
 120        exp.DataType.Type.INET: "INET",
 121    }
 122
 123    STAR_MAPPING = {
 124        "except": "EXCEPT",
 125        "replace": "REPLACE",
 126    }
 127
 128    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
 129
 130    STRUCT_DELIMITER = ("<", ">")
 131
 132    PARAMETER_TOKEN = "@"
 133
 134    PROPERTIES_LOCATION = {
 135        exp.AfterJournalProperty: exp.Properties.Location.POST_NAME,
 136        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
 137        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
 138        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
 139        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
 140        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
 141        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
 142        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
 143        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
 144        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
 145        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
 146        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
 147        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
 148        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
 149        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
 150        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
 151        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
 152        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
 153        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
 154        exp.JournalProperty: exp.Properties.Location.POST_NAME,
 155        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
 156        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
 157        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
 158        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
 159        exp.LogProperty: exp.Properties.Location.POST_NAME,
 160        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 161        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 162        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 163        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 164        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 165        exp.Property: exp.Properties.Location.POST_WITH,
 166        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 167        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 168        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 169        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 170        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 171        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 172        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 173        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 174        exp.TableFormatProperty: exp.Properties.Location.POST_WITH,
 175        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 176        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 177        exp.VolatilityProperty: exp.Properties.Location.POST_SCHEMA,
 178        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 179        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 180    }
 181
 182    WITH_SEPARATED_COMMENTS = (exp.Select, exp.From, exp.Where, exp.Binary)
 183    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 184
 185    __slots__ = (
 186        "time_mapping",
 187        "time_trie",
 188        "pretty",
 189        "quote_start",
 190        "quote_end",
 191        "identifier_start",
 192        "identifier_end",
 193        "identify",
 194        "normalize",
 195        "string_escape",
 196        "identifier_escape",
 197        "pad",
 198        "index_offset",
 199        "unnest_column_only",
 200        "alias_post_tablesample",
 201        "normalize_functions",
 202        "unsupported_level",
 203        "unsupported_messages",
 204        "null_ordering",
 205        "max_unsupported",
 206        "_indent",
 207        "_escaped_quote_end",
 208        "_escaped_identifier_end",
 209        "_leading_comma",
 210        "_max_text_width",
 211        "_comments",
 212    )
 213
 214    def __init__(
 215        self,
 216        time_mapping=None,
 217        time_trie=None,
 218        pretty=None,
 219        quote_start=None,
 220        quote_end=None,
 221        identifier_start=None,
 222        identifier_end=None,
 223        identify=False,
 224        normalize=False,
 225        string_escape=None,
 226        identifier_escape=None,
 227        pad=2,
 228        indent=2,
 229        index_offset=0,
 230        unnest_column_only=False,
 231        alias_post_tablesample=False,
 232        normalize_functions="upper",
 233        unsupported_level=ErrorLevel.WARN,
 234        null_ordering=None,
 235        max_unsupported=3,
 236        leading_comma=False,
 237        max_text_width=80,
 238        comments=True,
 239    ):
 240        import sqlglot
 241
 242        self.time_mapping = time_mapping or {}
 243        self.time_trie = time_trie
 244        self.pretty = pretty if pretty is not None else sqlglot.pretty
 245        self.quote_start = quote_start or "'"
 246        self.quote_end = quote_end or "'"
 247        self.identifier_start = identifier_start or '"'
 248        self.identifier_end = identifier_end or '"'
 249        self.identify = identify
 250        self.normalize = normalize
 251        self.string_escape = string_escape or "'"
 252        self.identifier_escape = identifier_escape or '"'
 253        self.pad = pad
 254        self.index_offset = index_offset
 255        self.unnest_column_only = unnest_column_only
 256        self.alias_post_tablesample = alias_post_tablesample
 257        self.normalize_functions = normalize_functions
 258        self.unsupported_level = unsupported_level
 259        self.unsupported_messages = []
 260        self.max_unsupported = max_unsupported
 261        self.null_ordering = null_ordering
 262        self._indent = indent
 263        self._escaped_quote_end = self.string_escape + self.quote_end
 264        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
 265        self._leading_comma = leading_comma
 266        self._max_text_width = max_text_width
 267        self._comments = comments
 268
 269    def generate(self, expression: t.Optional[exp.Expression]) -> str:
 270        """
 271        Generates a SQL string by interpreting the given syntax tree.
 272
 273        Args
 274            expression: the syntax tree.
 275
 276        Returns
 277            the SQL string.
 278        """
 279        self.unsupported_messages = []
 280        sql = self.sql(expression).strip()
 281
 282        if self.unsupported_level == ErrorLevel.IGNORE:
 283            return sql
 284
 285        if self.unsupported_level == ErrorLevel.WARN:
 286            for msg in self.unsupported_messages:
 287                logger.warning(msg)
 288        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 289            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 290
 291        if self.pretty:
 292            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 293        return sql
 294
 295    def unsupported(self, message: str) -> None:
 296        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 297            raise UnsupportedError(message)
 298        self.unsupported_messages.append(message)
 299
 300    def sep(self, sep: str = " ") -> str:
 301        return f"{sep.strip()}\n" if self.pretty else sep
 302
 303    def seg(self, sql: str, sep: str = " ") -> str:
 304        return f"{self.sep(sep)}{sql}"
 305
 306    def pad_comment(self, comment: str) -> str:
 307        comment = " " + comment if comment[0].strip() else comment
 308        comment = comment + " " if comment[-1].strip() else comment
 309        return comment
 310
 311    def maybe_comment(self, sql: str, expression: exp.Expression) -> str:
 312        comments = expression.comments if self._comments else None
 313
 314        if not comments:
 315            return sql
 316
 317        sep = "\n" if self.pretty else " "
 318        comments_sql = sep.join(
 319            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 320        )
 321
 322        if not comments_sql:
 323            return sql
 324
 325        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 326            return f"{comments_sql}{self.sep()}{sql}"
 327
 328        return f"{sql} {comments_sql}"
 329
 330    def wrap(self, expression: exp.Expression | str) -> str:
 331        this_sql = self.indent(
 332            self.sql(expression)
 333            if isinstance(expression, (exp.Select, exp.Union))
 334            else self.sql(expression, "this"),
 335            level=1,
 336            pad=0,
 337        )
 338        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 339
 340    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 341        original = self.identify
 342        self.identify = False
 343        result = func(*args, **kwargs)
 344        self.identify = original
 345        return result
 346
 347    def normalize_func(self, name: str) -> str:
 348        if self.normalize_functions == "upper":
 349            return name.upper()
 350        if self.normalize_functions == "lower":
 351            return name.lower()
 352        return name
 353
 354    def indent(
 355        self,
 356        sql: str,
 357        level: int = 0,
 358        pad: t.Optional[int] = None,
 359        skip_first: bool = False,
 360        skip_last: bool = False,
 361    ) -> str:
 362        if not self.pretty:
 363            return sql
 364
 365        pad = self.pad if pad is None else pad
 366        lines = sql.split("\n")
 367
 368        return "\n".join(
 369            line
 370            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 371            else f"{' ' * (level * self._indent + pad)}{line}"
 372            for i, line in enumerate(lines)
 373        )
 374
 375    def sql(
 376        self,
 377        expression: t.Optional[str | exp.Expression],
 378        key: t.Optional[str] = None,
 379        comment: bool = True,
 380    ) -> str:
 381        if not expression:
 382            return ""
 383
 384        if isinstance(expression, str):
 385            return expression
 386
 387        if key:
 388            return self.sql(expression.args.get(key))
 389
 390        transform = self.TRANSFORMS.get(expression.__class__)
 391
 392        if callable(transform):
 393            sql = transform(self, expression)
 394        elif transform:
 395            sql = transform
 396        elif isinstance(expression, exp.Expression):
 397            exp_handler_name = f"{expression.key}_sql"
 398
 399            if hasattr(self, exp_handler_name):
 400                sql = getattr(self, exp_handler_name)(expression)
 401            elif isinstance(expression, exp.Func):
 402                sql = self.function_fallback_sql(expression)
 403            elif isinstance(expression, exp.Property):
 404                sql = self.property_sql(expression)
 405            else:
 406                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 407        else:
 408            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 409
 410        return self.maybe_comment(sql, expression) if self._comments and comment else sql
 411
 412    def uncache_sql(self, expression: exp.Uncache) -> str:
 413        table = self.sql(expression, "this")
 414        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 415        return f"UNCACHE TABLE{exists_sql} {table}"
 416
 417    def cache_sql(self, expression: exp.Cache) -> str:
 418        lazy = " LAZY" if expression.args.get("lazy") else ""
 419        table = self.sql(expression, "this")
 420        options = expression.args.get("options")
 421        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 422        sql = self.sql(expression, "expression")
 423        sql = f" AS{self.sep()}{sql}" if sql else ""
 424        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 425        return self.prepend_ctes(expression, sql)
 426
 427    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 428        if isinstance(expression.parent, exp.Cast):
 429            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 430        default = "DEFAULT " if expression.args.get("default") else ""
 431        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 432
 433    def column_sql(self, expression: exp.Column) -> str:
 434        return ".".join(
 435            self.sql(part)
 436            for part in (
 437                expression.args.get("catalog"),
 438                expression.args.get("db"),
 439                expression.args.get("table"),
 440                expression.args.get("this"),
 441            )
 442            if part
 443        )
 444
 445    def columndef_sql(self, expression: exp.ColumnDef) -> str:
 446        column = self.sql(expression, "this")
 447        kind = self.sql(expression, "kind")
 448        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 449        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 450        kind = f" {kind}" if kind else ""
 451        constraints = f" {constraints}" if constraints else ""
 452
 453        return f"{exists}{column}{kind}{constraints}"
 454
 455    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 456        this = self.sql(expression, "this")
 457        kind_sql = self.sql(expression, "kind")
 458        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 459
 460    def autoincrementcolumnconstraint_sql(self, _) -> str:
 461        return self.token_sql(TokenType.AUTO_INCREMENT)
 462
 463    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
 464        if isinstance(expression.this, list):
 465            this = self.wrap(self.expressions(expression, key="this", flat=True))
 466        else:
 467            this = self.sql(expression, "this")
 468
 469        return f"COMPRESS {this}"
 470
 471    def generatedasidentitycolumnconstraint_sql(
 472        self, expression: exp.GeneratedAsIdentityColumnConstraint
 473    ) -> str:
 474        this = ""
 475        if expression.this is not None:
 476            this = " ALWAYS " if expression.this else " BY DEFAULT "
 477        start = expression.args.get("start")
 478        start = f"START WITH {start}" if start else ""
 479        increment = expression.args.get("increment")
 480        increment = f" INCREMENT BY {increment}" if increment else ""
 481        minvalue = expression.args.get("minvalue")
 482        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 483        maxvalue = expression.args.get("maxvalue")
 484        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 485        cycle = expression.args.get("cycle")
 486        cycle_sql = ""
 487        if cycle is not None:
 488            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
 489            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
 490        sequence_opts = ""
 491        if start or increment or cycle_sql:
 492            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
 493            sequence_opts = f" ({sequence_opts.strip()})"
 494        return f"GENERATED{this}AS IDENTITY{sequence_opts}"
 495
 496    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
 497        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
 498
 499    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
 500        desc = expression.args.get("desc")
 501        if desc is not None:
 502            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
 503        return f"PRIMARY KEY"
 504
 505    def uniquecolumnconstraint_sql(self, _) -> str:
 506        return "UNIQUE"
 507
 508    def create_sql(self, expression: exp.Create) -> str:
 509        kind = self.sql(expression, "kind").upper()
 510        properties = expression.args.get("properties")
 511        properties_exp = expression.copy()
 512        properties_locs = self.locate_properties(properties) if properties else {}
 513        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
 514            exp.Properties.Location.POST_WITH
 515        ):
 516            properties_exp.set(
 517                "properties",
 518                exp.Properties(
 519                    expressions=[
 520                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
 521                        *properties_locs[exp.Properties.Location.POST_WITH],
 522                    ]
 523                ),
 524            )
 525        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
 526            this_name = self.sql(expression.this, "this")
 527            this_properties = self.properties(
 528                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
 529                wrapped=False,
 530            )
 531            this_schema = f"({self.expressions(expression.this)})"
 532            this = f"{this_name}, {this_properties} {this_schema}"
 533            properties_sql = ""
 534        else:
 535            this = self.sql(expression, "this")
 536            properties_sql = self.sql(properties_exp, "properties")
 537        begin = " BEGIN" if expression.args.get("begin") else ""
 538        expression_sql = self.sql(expression, "expression")
 539        if expression_sql:
 540            expression_sql = f"{begin}{self.sep()}{expression_sql}"
 541
 542            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
 543                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
 544                    postalias_props_sql = self.properties(
 545                        exp.Properties(
 546                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
 547                        ),
 548                        wrapped=False,
 549                    )
 550                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
 551                else:
 552                    expression_sql = f" AS{expression_sql}"
 553
 554        postindex_props_sql = ""
 555        if properties_locs.get(exp.Properties.Location.POST_INDEX):
 556            postindex_props_sql = self.properties(
 557                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
 558                wrapped=False,
 559                prefix=" ",
 560            )
 561
 562        indexes = expression.args.get("indexes")
 563        if indexes:
 564            indexes_sql: t.List[str] = []
 565            for index in indexes:
 566                ind_unique = " UNIQUE" if index.args.get("unique") else ""
 567                ind_primary = " PRIMARY" if index.args.get("primary") else ""
 568                ind_amp = " AMP" if index.args.get("amp") else ""
 569                ind_name = f" {index.name}" if index.name else ""
 570                ind_columns = (
 571                    f' ({self.expressions(index, key="columns", flat=True)})'
 572                    if index.args.get("columns")
 573                    else ""
 574                )
 575                ind_sql = f"{ind_unique}{ind_primary}{ind_amp} INDEX{ind_name}{ind_columns}"
 576
 577                if indexes_sql:
 578                    indexes_sql.append(ind_sql)
 579                else:
 580                    indexes_sql.append(
 581                        f"{ind_sql}{postindex_props_sql}"
 582                        if index.args.get("primary")
 583                        else f"{postindex_props_sql}{ind_sql}"
 584                    )
 585
 586            index_sql = "".join(indexes_sql)
 587        else:
 588            index_sql = postindex_props_sql
 589
 590        replace = " OR REPLACE" if expression.args.get("replace") else ""
 591        unique = " UNIQUE" if expression.args.get("unique") else ""
 592        volatile = " VOLATILE" if expression.args.get("volatile") else ""
 593
 594        postcreate_props_sql = ""
 595        if properties_locs.get(exp.Properties.Location.POST_CREATE):
 596            postcreate_props_sql = self.properties(
 597                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
 598                sep=" ",
 599                prefix=" ",
 600                wrapped=False,
 601            )
 602
 603        modifiers = "".join((replace, unique, volatile, postcreate_props_sql))
 604
 605        postexpression_props_sql = ""
 606        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
 607            postexpression_props_sql = self.properties(
 608                exp.Properties(
 609                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
 610                ),
 611                sep=" ",
 612                prefix=" ",
 613                wrapped=False,
 614            )
 615
 616        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
 617        no_schema_binding = (
 618            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
 619        )
 620
 621        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}"
 622        return self.prepend_ctes(expression, expression_sql)
 623
 624    def describe_sql(self, expression: exp.Describe) -> str:
 625        return f"DESCRIBE {self.sql(expression, 'this')}"
 626
 627    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
 628        with_ = self.sql(expression, "with")
 629        if with_:
 630            sql = f"{with_}{self.sep()}{sql}"
 631        return sql
 632
 633    def with_sql(self, expression: exp.With) -> str:
 634        sql = self.expressions(expression, flat=True)
 635        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
 636
 637        return f"WITH {recursive}{sql}"
 638
 639    def cte_sql(self, expression: exp.CTE) -> str:
 640        alias = self.sql(expression, "alias")
 641        return f"{alias} AS {self.wrap(expression)}"
 642
 643    def tablealias_sql(self, expression: exp.TableAlias) -> str:
 644        alias = self.sql(expression, "this")
 645        columns = self.expressions(expression, key="columns", flat=True)
 646        columns = f"({columns})" if columns else ""
 647        return f"{alias}{columns}"
 648
 649    def bitstring_sql(self, expression: exp.BitString) -> str:
 650        return self.sql(expression, "this")
 651
 652    def hexstring_sql(self, expression: exp.HexString) -> str:
 653        return self.sql(expression, "this")
 654
 655    def datatype_sql(self, expression: exp.DataType) -> str:
 656        type_value = expression.this
 657        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
 658        nested = ""
 659        interior = self.expressions(expression, flat=True)
 660        values = ""
 661        if interior:
 662            if expression.args.get("nested"):
 663                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
 664                if expression.args.get("values") is not None:
 665                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
 666                    values = (
 667                        f"{delimiters[0]}{self.expressions(expression, 'values')}{delimiters[1]}"
 668                    )
 669            else:
 670                nested = f"({interior})"
 671
 672        return f"{type_sql}{nested}{values}"
 673
 674    def directory_sql(self, expression: exp.Directory) -> str:
 675        local = "LOCAL " if expression.args.get("local") else ""
 676        row_format = self.sql(expression, "row_format")
 677        row_format = f" {row_format}" if row_format else ""
 678        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
 679
 680    def delete_sql(self, expression: exp.Delete) -> str:
 681        this = self.sql(expression, "this")
 682        this = f" FROM {this}" if this else ""
 683        using_sql = (
 684            f" USING {self.expressions(expression, 'using', sep=', USING ')}"
 685            if expression.args.get("using")
 686            else ""
 687        )
 688        where_sql = self.sql(expression, "where")
 689        sql = f"DELETE{this}{using_sql}{where_sql}"
 690        return self.prepend_ctes(expression, sql)
 691
 692    def drop_sql(self, expression: exp.Drop) -> str:
 693        this = self.sql(expression, "this")
 694        kind = expression.args["kind"]
 695        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
 696        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
 697        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
 698        cascade = " CASCADE" if expression.args.get("cascade") else ""
 699        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}"
 700
 701    def except_sql(self, expression: exp.Except) -> str:
 702        return self.prepend_ctes(
 703            expression,
 704            self.set_operation(expression, self.except_op(expression)),
 705        )
 706
 707    def except_op(self, expression: exp.Except) -> str:
 708        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
 709
 710    def fetch_sql(self, expression: exp.Fetch) -> str:
 711        direction = expression.args.get("direction")
 712        direction = f" {direction.upper()}" if direction else ""
 713        count = expression.args.get("count")
 714        count = f" {count}" if count else ""
 715        return f"{self.seg('FETCH')}{direction}{count} ROWS ONLY"
 716
 717    def filter_sql(self, expression: exp.Filter) -> str:
 718        this = self.sql(expression, "this")
 719        where = self.sql(expression, "expression")[1:]  # where has a leading space
 720        return f"{this} FILTER({where})"
 721
 722    def hint_sql(self, expression: exp.Hint) -> str:
 723        if self.sql(expression, "this"):
 724            self.unsupported("Hints are not supported")
 725        return ""
 726
 727    def index_sql(self, expression: exp.Index) -> str:
 728        this = self.sql(expression, "this")
 729        table = self.sql(expression, "table")
 730        columns = self.sql(expression, "columns")
 731        return f"{this} ON {table} {columns}"
 732
 733    def identifier_sql(self, expression: exp.Identifier) -> str:
 734        text = expression.name
 735        text = text.lower() if self.normalize else text
 736        text = text.replace(self.identifier_end, self._escaped_identifier_end)
 737        if expression.args.get("quoted") or self.identify:
 738            text = f"{self.identifier_start}{text}{self.identifier_end}"
 739        return text
 740
 741    def national_sql(self, expression: exp.National) -> str:
 742        return f"N{self.sql(expression, 'this')}"
 743
 744    def partition_sql(self, expression: exp.Partition) -> str:
 745        return f"PARTITION({self.expressions(expression)})"
 746
 747    def properties_sql(self, expression: exp.Properties) -> str:
 748        root_properties = []
 749        with_properties = []
 750
 751        for p in expression.expressions:
 752            p_loc = self.PROPERTIES_LOCATION[p.__class__]
 753            if p_loc == exp.Properties.Location.POST_WITH:
 754                with_properties.append(p)
 755            elif p_loc == exp.Properties.Location.POST_SCHEMA:
 756                root_properties.append(p)
 757
 758        return self.root_properties(
 759            exp.Properties(expressions=root_properties)
 760        ) + self.with_properties(exp.Properties(expressions=with_properties))
 761
 762    def root_properties(self, properties: exp.Properties) -> str:
 763        if properties.expressions:
 764            return self.sep() + self.expressions(properties, indent=False, sep=" ")
 765        return ""
 766
 767    def properties(
 768        self,
 769        properties: exp.Properties,
 770        prefix: str = "",
 771        sep: str = ", ",
 772        suffix: str = "",
 773        wrapped: bool = True,
 774    ) -> str:
 775        if properties.expressions:
 776            expressions = self.expressions(properties, sep=sep, indent=False)
 777            expressions = self.wrap(expressions) if wrapped else expressions
 778            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
 779        return ""
 780
 781    def with_properties(self, properties: exp.Properties) -> str:
 782        return self.properties(properties, prefix=self.seg("WITH"))
 783
 784    def locate_properties(
 785        self, properties: exp.Properties
 786    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
 787        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
 788            key: [] for key in exp.Properties.Location
 789        }
 790
 791        for p in properties.expressions:
 792            p_loc = self.PROPERTIES_LOCATION[p.__class__]
 793            if p_loc == exp.Properties.Location.POST_NAME:
 794                properties_locs[exp.Properties.Location.POST_NAME].append(p)
 795            elif p_loc == exp.Properties.Location.POST_INDEX:
 796                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
 797            elif p_loc == exp.Properties.Location.POST_SCHEMA:
 798                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
 799            elif p_loc == exp.Properties.Location.POST_WITH:
 800                properties_locs[exp.Properties.Location.POST_WITH].append(p)
 801            elif p_loc == exp.Properties.Location.POST_CREATE:
 802                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
 803            elif p_loc == exp.Properties.Location.POST_ALIAS:
 804                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
 805            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
 806                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
 807            elif p_loc == exp.Properties.Location.UNSUPPORTED:
 808                self.unsupported(f"Unsupported property {p.key}")
 809
 810        return properties_locs
 811
 812    def property_sql(self, expression: exp.Property) -> str:
 813        property_cls = expression.__class__
 814        if property_cls == exp.Property:
 815            return f"{expression.name}={self.sql(expression, 'value')}"
 816
 817        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
 818        if not property_name:
 819            self.unsupported(f"Unsupported property {expression.key}")
 820
 821        return f"{property_name}={self.sql(expression, 'this')}"
 822
 823    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
 824        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
 825        options = f" {options}" if options else ""
 826        return f"LIKE {self.sql(expression, 'this')}{options}"
 827
 828    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
 829        no = "NO " if expression.args.get("no") else ""
 830        protection = " PROTECTION" if expression.args.get("protection") else ""
 831        return f"{no}FALLBACK{protection}"
 832
 833    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
 834        no = "NO " if expression.args.get("no") else ""
 835        dual = "DUAL " if expression.args.get("dual") else ""
 836        before = "BEFORE " if expression.args.get("before") else ""
 837        return f"{no}{dual}{before}JOURNAL"
 838
 839    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
 840        freespace = self.sql(expression, "this")
 841        percent = " PERCENT" if expression.args.get("percent") else ""
 842        return f"FREESPACE={freespace}{percent}"
 843
 844    def afterjournalproperty_sql(self, expression: exp.AfterJournalProperty) -> str:
 845        no = "NO " if expression.args.get("no") else ""
 846        dual = "DUAL " if expression.args.get("dual") else ""
 847        local = ""
 848        if expression.args.get("local") is not None:
 849            local = "LOCAL " if expression.args.get("local") else "NOT LOCAL "
 850        return f"{no}{dual}{local}AFTER JOURNAL"
 851
 852    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
 853        if expression.args.get("default"):
 854            property = "DEFAULT"
 855        elif expression.args.get("on"):
 856            property = "ON"
 857        else:
 858            property = "OFF"
 859        return f"CHECKSUM={property}"
 860
 861    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
 862        if expression.args.get("no"):
 863            return "NO MERGEBLOCKRATIO"
 864        if expression.args.get("default"):
 865            return "DEFAULT MERGEBLOCKRATIO"
 866
 867        percent = " PERCENT" if expression.args.get("percent") else ""
 868        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
 869
 870    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
 871        default = expression.args.get("default")
 872        min = expression.args.get("min")
 873        if default is not None or min is not None:
 874            if default:
 875                property = "DEFAULT"
 876            elif min:
 877                property = "MINIMUM"
 878            else:
 879                property = "MAXIMUM"
 880            return f"{property} DATABLOCKSIZE"
 881        else:
 882            units = expression.args.get("units")
 883            units = f" {units}" if units else ""
 884            return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
 885
 886    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
 887        autotemp = expression.args.get("autotemp")
 888        always = expression.args.get("always")
 889        default = expression.args.get("default")
 890        manual = expression.args.get("manual")
 891        never = expression.args.get("never")
 892
 893        if autotemp is not None:
 894            property = f"AUTOTEMP({self.expressions(autotemp)})"
 895        elif always:
 896            property = "ALWAYS"
 897        elif default:
 898            property = "DEFAULT"
 899        elif manual:
 900            property = "MANUAL"
 901        elif never:
 902            property = "NEVER"
 903        return f"BLOCKCOMPRESSION={property}"
 904
 905    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
 906        no = expression.args.get("no")
 907        no = " NO" if no else ""
 908        concurrent = expression.args.get("concurrent")
 909        concurrent = " CONCURRENT" if concurrent else ""
 910
 911        for_ = ""
 912        if expression.args.get("for_all"):
 913            for_ = " FOR ALL"
 914        elif expression.args.get("for_insert"):
 915            for_ = " FOR INSERT"
 916        elif expression.args.get("for_none"):
 917            for_ = " FOR NONE"
 918        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
 919
 920    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
 921        kind = expression.args.get("kind")
 922        this: str = f" {this}" if expression.this else ""
 923        for_or_in = expression.args.get("for_or_in")
 924        lock_type = expression.args.get("lock_type")
 925        override = " OVERRIDE" if expression.args.get("override") else ""
 926        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
 927
 928    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
 929        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
 930        statistics = expression.args.get("statistics")
 931        statistics_sql = ""
 932        if statistics is not None:
 933            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
 934        return f"{data_sql}{statistics_sql}"
 935
 936    def insert_sql(self, expression: exp.Insert) -> str:
 937        overwrite = expression.args.get("overwrite")
 938
 939        if isinstance(expression.this, exp.Directory):
 940            this = "OVERWRITE " if overwrite else "INTO "
 941        else:
 942            this = "OVERWRITE TABLE " if overwrite else "INTO "
 943
 944        alternative = expression.args.get("alternative")
 945        alternative = f" OR {alternative} " if alternative else " "
 946        this = f"{this}{self.sql(expression, 'this')}"
 947
 948        exists = " IF EXISTS " if expression.args.get("exists") else " "
 949        partition_sql = (
 950            self.sql(expression, "partition") if expression.args.get("partition") else ""
 951        )
 952        expression_sql = self.sql(expression, "expression")
 953        sep = self.sep() if partition_sql else ""
 954        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}"
 955        return self.prepend_ctes(expression, sql)
 956
 957    def intersect_sql(self, expression: exp.Intersect) -> str:
 958        return self.prepend_ctes(
 959            expression,
 960            self.set_operation(expression, self.intersect_op(expression)),
 961        )
 962
 963    def intersect_op(self, expression: exp.Intersect) -> str:
 964        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
 965
 966    def introducer_sql(self, expression: exp.Introducer) -> str:
 967        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
 968
 969    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
 970        return expression.name.upper()
 971
 972    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
 973        fields = expression.args.get("fields")
 974        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
 975        escaped = expression.args.get("escaped")
 976        escaped = f" ESCAPED BY {escaped}" if escaped else ""
 977        items = expression.args.get("collection_items")
 978        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
 979        keys = expression.args.get("map_keys")
 980        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
 981        lines = expression.args.get("lines")
 982        lines = f" LINES TERMINATED BY {lines}" if lines else ""
 983        null = expression.args.get("null")
 984        null = f" NULL DEFINED AS {null}" if null else ""
 985        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
 986
 987    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
 988        table = ".".join(
 989            part
 990            for part in [
 991                self.sql(expression, "catalog"),
 992                self.sql(expression, "db"),
 993                self.sql(expression, "this"),
 994            ]
 995            if part
 996        )
 997
 998        alias = self.sql(expression, "alias")
 999        alias = f"{sep}{alias}" if alias else ""
1000        hints = self.expressions(expression, key="hints", sep=", ", flat=True)
1001        hints = f" WITH ({hints})" if hints else ""
1002        laterals = self.expressions(expression, key="laterals", sep="")
1003        joins = self.expressions(expression, key="joins", sep="")
1004        pivots = self.expressions(expression, key="pivots", sep="")
1005        system_time = expression.args.get("system_time")
1006        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
1007
1008        return f"{table}{system_time}{alias}{hints}{laterals}{joins}{pivots}"
1009
1010    def tablesample_sql(self, expression: exp.TableSample) -> str:
1011        if self.alias_post_tablesample and expression.this.alias:
1012            this = self.sql(expression.this, "this")
1013            alias = f" AS {self.sql(expression.this, 'alias')}"
1014        else:
1015            this = self.sql(expression, "this")
1016            alias = ""
1017        method = self.sql(expression, "method")
1018        method = f" {method.upper()} " if method else ""
1019        numerator = self.sql(expression, "bucket_numerator")
1020        denominator = self.sql(expression, "bucket_denominator")
1021        field = self.sql(expression, "bucket_field")
1022        field = f" ON {field}" if field else ""
1023        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1024        percent = self.sql(expression, "percent")
1025        percent = f"{percent} PERCENT" if percent else ""
1026        rows = self.sql(expression, "rows")
1027        rows = f"{rows} ROWS" if rows else ""
1028        size = self.sql(expression, "size")
1029        seed = self.sql(expression, "seed")
1030        seed = f" SEED ({seed})" if seed else ""
1031        return f"{this} TABLESAMPLE{method}({bucket}{percent}{rows}{size}){seed}{alias}"
1032
1033    def pivot_sql(self, expression: exp.Pivot) -> str:
1034        this = self.sql(expression, "this")
1035        alias = self.sql(expression, "alias")
1036        alias = f" AS {alias}" if alias else ""
1037        unpivot = expression.args.get("unpivot")
1038        direction = "UNPIVOT" if unpivot else "PIVOT"
1039        expressions = self.expressions(expression, key="expressions")
1040        field = self.sql(expression, "field")
1041        return f"{this} {direction}({expressions} FOR {field}){alias}"
1042
1043    def tuple_sql(self, expression: exp.Tuple) -> str:
1044        return f"({self.expressions(expression, flat=True)})"
1045
1046    def update_sql(self, expression: exp.Update) -> str:
1047        this = self.sql(expression, "this")
1048        set_sql = self.expressions(expression, flat=True)
1049        from_sql = self.sql(expression, "from")
1050        where_sql = self.sql(expression, "where")
1051        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}"
1052        return self.prepend_ctes(expression, sql)
1053
1054    def values_sql(self, expression: exp.Values) -> str:
1055        args = self.expressions(expression)
1056        alias = self.sql(expression, "alias")
1057        values = f"VALUES{self.seg('')}{args}"
1058        values = (
1059            f"({values})"
1060            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1061            else values
1062        )
1063        return f"{values} AS {alias}" if alias else values
1064
1065    def var_sql(self, expression: exp.Var) -> str:
1066        return self.sql(expression, "this")
1067
1068    def into_sql(self, expression: exp.Into) -> str:
1069        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1070        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1071        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
1072
1073    def from_sql(self, expression: exp.From) -> str:
1074        expressions = self.expressions(expression, flat=True)
1075        return f"{self.seg('FROM')} {expressions}"
1076
1077    def group_sql(self, expression: exp.Group) -> str:
1078        group_by = self.op_expressions("GROUP BY", expression)
1079        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1080        grouping_sets = (
1081            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1082        )
1083
1084        cube = expression.args.get("cube", [])
1085        if seq_get(cube, 0) is True:
1086            return f"{group_by}{self.seg('WITH CUBE')}"
1087        else:
1088            cube_sql = self.expressions(expression, key="cube", indent=False)
1089            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1090
1091        rollup = expression.args.get("rollup", [])
1092        if seq_get(rollup, 0) is True:
1093            return f"{group_by}{self.seg('WITH ROLLUP')}"
1094        else:
1095            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1096            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1097
1098        groupings = csv(grouping_sets, cube_sql, rollup_sql, sep=",")
1099
1100        if expression.args.get("expressions") and groupings:
1101            group_by = f"{group_by},"
1102
1103        return f"{group_by}{groupings}"
1104
1105    def having_sql(self, expression: exp.Having) -> str:
1106        this = self.indent(self.sql(expression, "this"))
1107        return f"{self.seg('HAVING')}{self.sep()}{this}"
1108
1109    def join_sql(self, expression: exp.Join) -> str:
1110        op_sql = self.seg(
1111            " ".join(
1112                op
1113                for op in (
1114                    "NATURAL" if expression.args.get("natural") else None,
1115                    expression.side,
1116                    expression.kind,
1117                    "JOIN",
1118                )
1119                if op
1120            )
1121        )
1122        on_sql = self.sql(expression, "on")
1123        using = expression.args.get("using")
1124
1125        if not on_sql and using:
1126            on_sql = csv(*(self.sql(column) for column in using))
1127
1128        if on_sql:
1129            on_sql = self.indent(on_sql, skip_first=True)
1130            space = self.seg(" " * self.pad) if self.pretty else " "
1131            if using:
1132                on_sql = f"{space}USING ({on_sql})"
1133            else:
1134                on_sql = f"{space}ON {on_sql}"
1135
1136        expression_sql = self.sql(expression, "expression")
1137        this_sql = self.sql(expression, "this")
1138        return f"{expression_sql}{op_sql} {this_sql}{on_sql}"
1139
1140    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1141        args = self.expressions(expression, flat=True)
1142        args = f"({args})" if len(args.split(",")) > 1 else args
1143        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
1144
1145    def lateral_sql(self, expression: exp.Lateral) -> str:
1146        this = self.sql(expression, "this")
1147
1148        if isinstance(expression.this, exp.Subquery):
1149            return f"LATERAL {this}"
1150
1151        if expression.args.get("view"):
1152            alias = expression.args["alias"]
1153            columns = self.expressions(alias, key="columns", flat=True)
1154            table = f" {alias.name}" if alias.name else ""
1155            columns = f" AS {columns}" if columns else ""
1156            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1157            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1158
1159        alias = self.sql(expression, "alias")
1160        alias = f" AS {alias}" if alias else ""
1161        return f"LATERAL {this}{alias}"
1162
1163    def limit_sql(self, expression: exp.Limit) -> str:
1164        this = self.sql(expression, "this")
1165        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"
1166
1167    def offset_sql(self, expression: exp.Offset) -> str:
1168        this = self.sql(expression, "this")
1169        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
1170
1171    def lock_sql(self, expression: exp.Lock) -> str:
1172        if self.LOCKING_READS_SUPPORTED:
1173            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
1174            return self.seg(f"FOR {lock_type}")
1175
1176        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1177        return ""
1178
1179    def literal_sql(self, expression: exp.Literal) -> str:
1180        text = expression.this or ""
1181        if expression.is_string:
1182            text = text.replace(self.quote_end, self._escaped_quote_end)
1183            if self.pretty:
1184                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1185            text = f"{self.quote_start}{text}{self.quote_end}"
1186        return text
1187
1188    def loaddata_sql(self, expression: exp.LoadData) -> str:
1189        local = " LOCAL" if expression.args.get("local") else ""
1190        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1191        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1192        this = f" INTO TABLE {self.sql(expression, 'this')}"
1193        partition = self.sql(expression, "partition")
1194        partition = f" {partition}" if partition else ""
1195        input_format = self.sql(expression, "input_format")
1196        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1197        serde = self.sql(expression, "serde")
1198        serde = f" SERDE {serde}" if serde else ""
1199        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
1200
1201    def null_sql(self, *_) -> str:
1202        return "NULL"
1203
1204    def boolean_sql(self, expression: exp.Boolean) -> str:
1205        return "TRUE" if expression.this else "FALSE"
1206
1207    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1208        this = self.sql(expression, "this")
1209        this = f"{this} " if this else this
1210        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
1211
1212    def cluster_sql(self, expression: exp.Cluster) -> str:
1213        return self.op_expressions("CLUSTER BY", expression)
1214
1215    def distribute_sql(self, expression: exp.Distribute) -> str:
1216        return self.op_expressions("DISTRIBUTE BY", expression)
1217
1218    def sort_sql(self, expression: exp.Sort) -> str:
1219        return self.op_expressions("SORT BY", expression)
1220
1221    def ordered_sql(self, expression: exp.Ordered) -> str:
1222        desc = expression.args.get("desc")
1223        asc = not desc
1224
1225        nulls_first = expression.args.get("nulls_first")
1226        nulls_last = not nulls_first
1227        nulls_are_large = self.null_ordering == "nulls_are_large"
1228        nulls_are_small = self.null_ordering == "nulls_are_small"
1229        nulls_are_last = self.null_ordering == "nulls_are_last"
1230
1231        sort_order = " DESC" if desc else ""
1232        nulls_sort_change = ""
1233        if nulls_first and (
1234            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
1235        ):
1236            nulls_sort_change = " NULLS FIRST"
1237        elif (
1238            nulls_last
1239            and ((asc and nulls_are_small) or (desc and nulls_are_large))
1240            and not nulls_are_last
1241        ):
1242            nulls_sort_change = " NULLS LAST"
1243
1244        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
1245            self.unsupported(
1246                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
1247            )
1248            nulls_sort_change = ""
1249
1250        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
1251
1252    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
1253        partition = self.partition_by_sql(expression)
1254        order = self.sql(expression, "order")
1255        measures = self.sql(expression, "measures")
1256        measures = self.seg(f"MEASURES {measures}") if measures else ""
1257        rows = self.sql(expression, "rows")
1258        rows = self.seg(rows) if rows else ""
1259        after = self.sql(expression, "after")
1260        after = self.seg(after) if after else ""
1261        pattern = self.sql(expression, "pattern")
1262        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
1263        define = self.sql(expression, "define")
1264        define = self.seg(f"DEFINE {define}") if define else ""
1265        body = "".join(
1266            (
1267                partition,
1268                order,
1269                measures,
1270                rows,
1271                after,
1272                pattern,
1273                define,
1274            )
1275        )
1276        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"
1277
1278    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
1279        return csv(
1280            *sqls,
1281            *[self.sql(sql) for sql in expression.args.get("joins") or []],
1282            self.sql(expression, "match"),
1283            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
1284            self.sql(expression, "where"),
1285            self.sql(expression, "group"),
1286            self.sql(expression, "having"),
1287            self.sql(expression, "qualify"),
1288            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
1289            if expression.args.get("windows")
1290            else "",
1291            self.sql(expression, "distribute"),
1292            self.sql(expression, "sort"),
1293            self.sql(expression, "cluster"),
1294            self.sql(expression, "order"),
1295            self.sql(expression, "limit"),
1296            self.sql(expression, "offset"),
1297            self.sql(expression, "lock"),
1298            sep="",
1299        )
1300
1301    def select_sql(self, expression: exp.Select) -> str:
1302        hint = self.sql(expression, "hint")
1303        distinct = self.sql(expression, "distinct")
1304        distinct = f" {distinct}" if distinct else ""
1305        expressions = self.expressions(expression)
1306        expressions = f"{self.sep()}{expressions}" if expressions else expressions
1307        sql = self.query_modifiers(
1308            expression,
1309            f"SELECT{hint}{distinct}{expressions}",
1310            self.sql(expression, "into", comment=False),
1311            self.sql(expression, "from", comment=False),
1312        )
1313        return self.prepend_ctes(expression, sql)
1314
1315    def schema_sql(self, expression: exp.Schema) -> str:
1316        this = self.sql(expression, "this")
1317        this = f"{this} " if this else ""
1318        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
1319        return f"{this}{sql}"
1320
1321    def star_sql(self, expression: exp.Star) -> str:
1322        except_ = self.expressions(expression, key="except", flat=True)
1323        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
1324        replace = self.expressions(expression, key="replace", flat=True)
1325        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
1326        return f"*{except_}{replace}"
1327
1328    def structkwarg_sql(self, expression: exp.StructKwarg) -> str:
1329        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1330
1331    def parameter_sql(self, expression: exp.Parameter) -> str:
1332        this = self.sql(expression, "this")
1333        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
1334        return f"{self.PARAMETER_TOKEN}{this}"
1335
1336    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
1337        this = self.sql(expression, "this")
1338        kind = expression.text("kind")
1339        if kind:
1340            kind = f"{kind}."
1341        return f"@@{kind}{this}"
1342
1343    def placeholder_sql(self, expression: exp.Placeholder) -> str:
1344        return f":{expression.name}" if expression.name else "?"
1345
1346    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
1347        alias = self.sql(expression, "alias")
1348        alias = f"{sep}{alias}" if alias else ""
1349
1350        sql = self.query_modifiers(
1351            expression,
1352            self.wrap(expression),
1353            self.expressions(expression, key="pivots", sep=" "),
1354            alias,
1355        )
1356
1357        return self.prepend_ctes(expression, sql)
1358
1359    def qualify_sql(self, expression: exp.Qualify) -> str:
1360        this = self.indent(self.sql(expression, "this"))
1361        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
1362
1363    def union_sql(self, expression: exp.Union) -> str:
1364        return self.prepend_ctes(
1365            expression,
1366            self.set_operation(expression, self.union_op(expression)),
1367        )
1368
1369    def union_op(self, expression: exp.Union) -> str:
1370        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
1371        kind = kind if expression.args.get("distinct") else " ALL"
1372        return f"UNION{kind}"
1373
1374    def unnest_sql(self, expression: exp.Unnest) -> str:
1375        args = self.expressions(expression, flat=True)
1376        alias = expression.args.get("alias")
1377        if alias and self.unnest_column_only:
1378            columns = alias.columns
1379            alias = self.sql(columns[0]) if columns else ""
1380        else:
1381            alias = self.sql(expression, "alias")
1382        alias = f" AS {alias}" if alias else alias
1383        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
1384        offset = expression.args.get("offset")
1385        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
1386        return f"UNNEST({args}){ordinality}{alias}{offset}"
1387
1388    def where_sql(self, expression: exp.Where) -> str:
1389        this = self.indent(self.sql(expression, "this"))
1390        return f"{self.seg('WHERE')}{self.sep()}{this}"
1391
1392    def window_sql(self, expression: exp.Window) -> str:
1393        this = self.sql(expression, "this")
1394
1395        partition = self.partition_by_sql(expression)
1396
1397        order = expression.args.get("order")
1398        order_sql = self.order_sql(order, flat=True) if order else ""
1399
1400        partition_sql = partition + " " if partition and order else partition
1401
1402        spec = expression.args.get("spec")
1403        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
1404
1405        alias = self.sql(expression, "alias")
1406        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
1407
1408        if not partition and not order and not spec and alias:
1409            return f"{this} {alias}"
1410
1411        window_args = alias + partition_sql + order_sql + spec_sql
1412
1413        return f"{this} ({window_args.strip()})"
1414
1415    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
1416        partition = self.expressions(expression, key="partition_by", flat=True)
1417        return f"PARTITION BY {partition}" if partition else ""
1418
1419    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
1420        kind = self.sql(expression, "kind")
1421        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
1422        end = (
1423            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
1424            or "CURRENT ROW"
1425        )
1426        return f"{kind} BETWEEN {start} AND {end}"
1427
1428    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
1429        this = self.sql(expression, "this")
1430        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
1431        return f"{this} WITHIN GROUP ({expression_sql})"
1432
1433    def between_sql(self, expression: exp.Between) -> str:
1434        this = self.sql(expression, "this")
1435        low = self.sql(expression, "low")
1436        high = self.sql(expression, "high")
1437        return f"{this} BETWEEN {low} AND {high}"
1438
1439    def bracket_sql(self, expression: exp.Bracket) -> str:
1440        expressions = apply_index_offset(expression.expressions, self.index_offset)
1441        expressions_sql = ", ".join(self.sql(e) for e in expressions)
1442
1443        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
1444
1445    def all_sql(self, expression: exp.All) -> str:
1446        return f"ALL {self.wrap(expression)}"
1447
1448    def any_sql(self, expression: exp.Any) -> str:
1449        this = self.sql(expression, "this")
1450        if isinstance(expression.this, exp.Subqueryable):
1451            this = self.wrap(this)
1452        return f"ANY {this}"
1453
1454    def exists_sql(self, expression: exp.Exists) -> str:
1455        return f"EXISTS{self.wrap(expression)}"
1456
1457    def case_sql(self, expression: exp.Case) -> str:
1458        this = self.sql(expression, "this")
1459        statements = [f"CASE {this}" if this else "CASE"]
1460
1461        for e in expression.args["ifs"]:
1462            statements.append(f"WHEN {self.sql(e, 'this')}")
1463            statements.append(f"THEN {self.sql(e, 'true')}")
1464
1465        default = self.sql(expression, "default")
1466
1467        if default:
1468            statements.append(f"ELSE {default}")
1469
1470        statements.append("END")
1471
1472        if self.pretty and self.text_width(statements) > self._max_text_width:
1473            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
1474
1475        return " ".join(statements)
1476
1477    def constraint_sql(self, expression: exp.Constraint) -> str:
1478        this = self.sql(expression, "this")
1479        expressions = self.expressions(expression, flat=True)
1480        return f"CONSTRAINT {this} {expressions}"
1481
1482    def extract_sql(self, expression: exp.Extract) -> str:
1483        this = self.sql(expression, "this")
1484        expression_sql = self.sql(expression, "expression")
1485        return f"EXTRACT({this} FROM {expression_sql})"
1486
1487    def trim_sql(self, expression: exp.Trim) -> str:
1488        trim_type = self.sql(expression, "position")
1489
1490        if trim_type == "LEADING":
1491            return self.func("LTRIM", expression.this)
1492        elif trim_type == "TRAILING":
1493            return self.func("RTRIM", expression.this)
1494        else:
1495            return self.func("TRIM", expression.this, expression.expression)
1496
1497    def concat_sql(self, expression: exp.Concat) -> str:
1498        if len(expression.expressions) == 1:
1499            return self.sql(expression.expressions[0])
1500        return self.function_fallback_sql(expression)
1501
1502    def check_sql(self, expression: exp.Check) -> str:
1503        this = self.sql(expression, key="this")
1504        return f"CHECK ({this})"
1505
1506    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
1507        expressions = self.expressions(expression, flat=True)
1508        reference = self.sql(expression, "reference")
1509        reference = f" {reference}" if reference else ""
1510        delete = self.sql(expression, "delete")
1511        delete = f" ON DELETE {delete}" if delete else ""
1512        update = self.sql(expression, "update")
1513        update = f" ON UPDATE {update}" if update else ""
1514        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
1515
1516    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
1517        expressions = self.expressions(expression, flat=True)
1518        options = self.expressions(expression, "options", flat=True, sep=" ")
1519        options = f" {options}" if options else ""
1520        return f"PRIMARY KEY ({expressions}){options}"
1521
1522    def unique_sql(self, expression: exp.Unique) -> str:
1523        columns = self.expressions(expression, key="expressions")
1524        return f"UNIQUE ({columns})"
1525
1526    def if_sql(self, expression: exp.If) -> str:
1527        return self.case_sql(
1528            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
1529        )
1530
1531    def in_sql(self, expression: exp.In) -> str:
1532        query = expression.args.get("query")
1533        unnest = expression.args.get("unnest")
1534        field = expression.args.get("field")
1535        is_global = " GLOBAL" if expression.args.get("is_global") else ""
1536
1537        if query:
1538            in_sql = self.wrap(query)
1539        elif unnest:
1540            in_sql = self.in_unnest_op(unnest)
1541        elif field:
1542            in_sql = self.sql(field)
1543        else:
1544            in_sql = f"({self.expressions(expression, flat=True)})"
1545
1546        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
1547
1548    def in_unnest_op(self, unnest: exp.Unnest) -> str:
1549        return f"(SELECT {self.sql(unnest)})"
1550
1551    def interval_sql(self, expression: exp.Interval) -> str:
1552        this = expression.args.get("this")
1553        if this:
1554            this = (
1555                f" {this}"
1556                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
1557                else f" ({this})"
1558            )
1559        else:
1560            this = ""
1561        unit = expression.args.get("unit")
1562        unit = f" {unit}" if unit else ""
1563        return f"INTERVAL{this}{unit}"
1564
1565    def return_sql(self, expression: exp.Return) -> str:
1566        return f"RETURN {self.sql(expression, 'this')}"
1567
1568    def reference_sql(self, expression: exp.Reference) -> str:
1569        this = self.sql(expression, "this")
1570        expressions = self.expressions(expression, flat=True)
1571        expressions = f"({expressions})" if expressions else ""
1572        options = self.expressions(expression, "options", flat=True, sep=" ")
1573        options = f" {options}" if options else ""
1574        return f"REFERENCES {this}{expressions}{options}"
1575
1576    def anonymous_sql(self, expression: exp.Anonymous) -> str:
1577        return self.func(expression.name, *expression.expressions)
1578
1579    def paren_sql(self, expression: exp.Paren) -> str:
1580        if isinstance(expression.unnest(), exp.Select):
1581            sql = self.wrap(expression)
1582        else:
1583            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
1584            sql = f"({sql}{self.seg(')', sep='')}"
1585
1586        return self.prepend_ctes(expression, sql)
1587
1588    def neg_sql(self, expression: exp.Neg) -> str:
1589        # This makes sure we don't convert "- - 5" to "--5", which is a comment
1590        this_sql = self.sql(expression, "this")
1591        sep = " " if this_sql[0] == "-" else ""
1592        return f"-{sep}{this_sql}"
1593
1594    def not_sql(self, expression: exp.Not) -> str:
1595        return f"NOT {self.sql(expression, 'this')}"
1596
1597    def alias_sql(self, expression: exp.Alias) -> str:
1598        to_sql = self.sql(expression, "alias")
1599        to_sql = f" AS {to_sql}" if to_sql else ""
1600        return f"{self.sql(expression, 'this')}{to_sql}"
1601
1602    def aliases_sql(self, expression: exp.Aliases) -> str:
1603        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
1604
1605    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
1606        this = self.sql(expression, "this")
1607        zone = self.sql(expression, "zone")
1608        return f"{this} AT TIME ZONE {zone}"
1609
1610    def add_sql(self, expression: exp.Add) -> str:
1611        return self.binary(expression, "+")
1612
1613    def and_sql(self, expression: exp.And) -> str:
1614        return self.connector_sql(expression, "AND")
1615
1616    def connector_sql(self, expression: exp.Connector, op: str) -> str:
1617        if not self.pretty:
1618            return self.binary(expression, op)
1619
1620        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
1621        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
1622        return f"{sep}{op} ".join(sqls)
1623
1624    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
1625        return self.binary(expression, "&")
1626
1627    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
1628        return self.binary(expression, "<<")
1629
1630    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
1631        return f"~{self.sql(expression, 'this')}"
1632
1633    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
1634        return self.binary(expression, "|")
1635
1636    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
1637        return self.binary(expression, ">>")
1638
1639    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
1640        return self.binary(expression, "^")
1641
1642    def cast_sql(self, expression: exp.Cast) -> str:
1643        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
1644
1645    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
1646        zone = self.sql(expression, "this")
1647        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
1648
1649    def collate_sql(self, expression: exp.Collate) -> str:
1650        return self.binary(expression, "COLLATE")
1651
1652    def command_sql(self, expression: exp.Command) -> str:
1653        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
1654
1655    def comment_sql(self, expression: exp.Comment) -> str:
1656        this = self.sql(expression, "this")
1657        kind = expression.args["kind"]
1658        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1659        expression_sql = self.sql(expression, "expression")
1660        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
1661
1662    def transaction_sql(self, *_) -> str:
1663        return "BEGIN"
1664
1665    def commit_sql(self, expression: exp.Commit) -> str:
1666        chain = expression.args.get("chain")
1667        if chain is not None:
1668            chain = " AND CHAIN" if chain else " AND NO CHAIN"
1669
1670        return f"COMMIT{chain or ''}"
1671
1672    def rollback_sql(self, expression: exp.Rollback) -> str:
1673        savepoint = expression.args.get("savepoint")
1674        savepoint = f" TO {savepoint}" if savepoint else ""
1675        return f"ROLLBACK{savepoint}"
1676
1677    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
1678        this = self.sql(expression, "this")
1679
1680        dtype = self.sql(expression, "dtype")
1681        if dtype:
1682            collate = self.sql(expression, "collate")
1683            collate = f" COLLATE {collate}" if collate else ""
1684            using = self.sql(expression, "using")
1685            using = f" USING {using}" if using else ""
1686            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
1687
1688        default = self.sql(expression, "default")
1689        if default:
1690            return f"ALTER COLUMN {this} SET DEFAULT {default}"
1691
1692        if not expression.args.get("drop"):
1693            self.unsupported("Unsupported ALTER COLUMN syntax")
1694
1695        return f"ALTER COLUMN {this} DROP DEFAULT"
1696
1697    def renametable_sql(self, expression: exp.RenameTable) -> str:
1698        this = self.sql(expression, "this")
1699        return f"RENAME TO {this}"
1700
1701    def altertable_sql(self, expression: exp.AlterTable) -> str:
1702        actions = expression.args["actions"]
1703
1704        if isinstance(actions[0], exp.ColumnDef):
1705            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
1706        elif isinstance(actions[0], exp.Schema):
1707            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
1708        elif isinstance(actions[0], exp.Delete):
1709            actions = self.expressions(expression, "actions", flat=True)
1710        else:
1711            actions = self.expressions(expression, "actions")
1712
1713        exists = " IF EXISTS" if expression.args.get("exists") else ""
1714        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
1715
1716    def droppartition_sql(self, expression: exp.DropPartition) -> str:
1717        expressions = self.expressions(expression)
1718        exists = " IF EXISTS " if expression.args.get("exists") else " "
1719        return f"DROP{exists}{expressions}"
1720
1721    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
1722        this = self.sql(expression, "this")
1723        expression_ = self.sql(expression, "expression")
1724        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
1725
1726        enforced = expression.args.get("enforced")
1727        if enforced is not None:
1728            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
1729
1730        return f"{add_constraint} {expression_}"
1731
1732    def distinct_sql(self, expression: exp.Distinct) -> str:
1733        this = self.expressions(expression, flat=True)
1734        this = f" {this}" if this else ""
1735
1736        on = self.sql(expression, "on")
1737        on = f" ON {on}" if on else ""
1738        return f"DISTINCT{this}{on}"
1739
1740    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
1741        return f"{self.sql(expression, 'this')} IGNORE NULLS"
1742
1743    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
1744        return f"{self.sql(expression, 'this')} RESPECT NULLS"
1745
1746    def intdiv_sql(self, expression: exp.IntDiv) -> str:
1747        return self.sql(
1748            exp.Cast(
1749                this=exp.Div(this=expression.this, expression=expression.expression),
1750                to=exp.DataType(this=exp.DataType.Type.INT),
1751            )
1752        )
1753
1754    def dpipe_sql(self, expression: exp.DPipe) -> str:
1755        return self.binary(expression, "||")
1756
1757    def div_sql(self, expression: exp.Div) -> str:
1758        return self.binary(expression, "/")
1759
1760    def overlaps_sql(self, expression: exp.Overlaps) -> str:
1761        return self.binary(expression, "OVERLAPS")
1762
1763    def distance_sql(self, expression: exp.Distance) -> str:
1764        return self.binary(expression, "<->")
1765
1766    def dot_sql(self, expression: exp.Dot) -> str:
1767        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
1768
1769    def eq_sql(self, expression: exp.EQ) -> str:
1770        return self.binary(expression, "=")
1771
1772    def escape_sql(self, expression: exp.Escape) -> str:
1773        return self.binary(expression, "ESCAPE")
1774
1775    def glob_sql(self, expression: exp.Glob) -> str:
1776        return self.binary(expression, "GLOB")
1777
1778    def gt_sql(self, expression: exp.GT) -> str:
1779        return self.binary(expression, ">")
1780
1781    def gte_sql(self, expression: exp.GTE) -> str:
1782        return self.binary(expression, ">=")
1783
1784    def ilike_sql(self, expression: exp.ILike) -> str:
1785        return self.binary(expression, "ILIKE")
1786
1787    def is_sql(self, expression: exp.Is) -> str:
1788        return self.binary(expression, "IS")
1789
1790    def like_sql(self, expression: exp.Like) -> str:
1791        return self.binary(expression, "LIKE")
1792
1793    def similarto_sql(self, expression: exp.SimilarTo) -> str:
1794        return self.binary(expression, "SIMILAR TO")
1795
1796    def lt_sql(self, expression: exp.LT) -> str:
1797        return self.binary(expression, "<")
1798
1799    def lte_sql(self, expression: exp.LTE) -> str:
1800        return self.binary(expression, "<=")
1801
1802    def mod_sql(self, expression: exp.Mod) -> str:
1803        return self.binary(expression, "%")
1804
1805    def mul_sql(self, expression: exp.Mul) -> str:
1806        return self.binary(expression, "*")
1807
1808    def neq_sql(self, expression: exp.NEQ) -> str:
1809        return self.binary(expression, "<>")
1810
1811    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
1812        return self.binary(expression, "IS NOT DISTINCT FROM")
1813
1814    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
1815        return self.binary(expression, "IS DISTINCT FROM")
1816
1817    def or_sql(self, expression: exp.Or) -> str:
1818        return self.connector_sql(expression, "OR")
1819
1820    def slice_sql(self, expression: exp.Slice) -> str:
1821        return self.binary(expression, ":")
1822
1823    def sub_sql(self, expression: exp.Sub) -> str:
1824        return self.binary(expression, "-")
1825
1826    def trycast_sql(self, expression: exp.TryCast) -> str:
1827        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
1828
1829    def use_sql(self, expression: exp.Use) -> str:
1830        kind = self.sql(expression, "kind")
1831        kind = f" {kind}" if kind else ""
1832        this = self.sql(expression, "this")
1833        this = f" {this}" if this else ""
1834        return f"USE{kind}{this}"
1835
1836    def binary(self, expression: exp.Binary, op: str) -> str:
1837        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
1838
1839    def function_fallback_sql(self, expression: exp.Func) -> str:
1840        args = []
1841        for arg_value in expression.args.values():
1842            if isinstance(arg_value, list):
1843                for value in arg_value:
1844                    args.append(value)
1845            else:
1846                args.append(arg_value)
1847
1848        return self.func(expression.sql_name(), *args)
1849
1850    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
1851        return f"{self.normalize_func(name)}({self.format_args(*args)})"
1852
1853    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
1854        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
1855        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
1856            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
1857        return ", ".join(arg_sqls)
1858
1859    def text_width(self, args: t.Iterable) -> int:
1860        return sum(len(arg) for arg in args)
1861
1862    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
1863        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)
1864
1865    def expressions(
1866        self,
1867        expression: exp.Expression,
1868        key: t.Optional[str] = None,
1869        flat: bool = False,
1870        indent: bool = True,
1871        sep: str = ", ",
1872        prefix: str = "",
1873    ) -> str:
1874        expressions = expression.args.get(key or "expressions")
1875
1876        if not expressions:
1877            return ""
1878
1879        if flat:
1880            return sep.join(self.sql(e) for e in expressions)
1881
1882        num_sqls = len(expressions)
1883
1884        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
1885        pad = " " * self.pad
1886        stripped_sep = sep.strip()
1887
1888        result_sqls = []
1889        for i, e in enumerate(expressions):
1890            sql = self.sql(e, comment=False)
1891            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
1892
1893            if self.pretty:
1894                if self._leading_comma:
1895                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
1896                else:
1897                    result_sqls.append(
1898                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
1899                    )
1900            else:
1901                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
1902
1903        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
1904        return self.indent(result_sql, skip_first=False) if indent else result_sql
1905
1906    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
1907        flat = flat or isinstance(expression.parent, exp.Properties)
1908        expressions_sql = self.expressions(expression, flat=flat)
1909        if flat:
1910            return f"{op} {expressions_sql}"
1911        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
1912
1913    def naked_property(self, expression: exp.Property) -> str:
1914        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
1915        if not property_name:
1916            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
1917        return f"{property_name} {self.sql(expression, 'this')}"
1918
1919    def set_operation(self, expression: exp.Expression, op: str) -> str:
1920        this = self.sql(expression, "this")
1921        op = self.seg(op)
1922        return self.query_modifiers(
1923            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
1924        )
1925
1926    def tag_sql(self, expression: exp.Tag) -> str:
1927        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
1928
1929    def token_sql(self, token_type: TokenType) -> str:
1930        return self.TOKEN_MAPPING.get(token_type, token_type.name)
1931
1932    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
1933        this = self.sql(expression, "this")
1934        expressions = self.no_identify(self.expressions, expression)
1935        expressions = (
1936            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
1937        )
1938        return f"{this}{expressions}"
1939
1940    def joinhint_sql(self, expression: exp.JoinHint) -> str:
1941        this = self.sql(expression, "this")
1942        expressions = self.expressions(expression, flat=True)
1943        return f"{this}({expressions})"
1944
1945    def kwarg_sql(self, expression: exp.Kwarg) -> str:
1946        return self.binary(expression, "=>")
1947
1948    def when_sql(self, expression: exp.When) -> str:
1949        this = self.sql(expression, "this")
1950        then_expression = expression.args.get("then")
1951        if isinstance(then_expression, exp.Insert):
1952            then = f"INSERT {self.sql(then_expression, 'this')}"
1953            if "expression" in then_expression.args:
1954                then += f" VALUES {self.sql(then_expression, 'expression')}"
1955        elif isinstance(then_expression, exp.Update):
1956            if isinstance(then_expression.args.get("expressions"), exp.Star):
1957                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
1958            else:
1959                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
1960        else:
1961            then = self.sql(then_expression)
1962        return f"WHEN {this} THEN {then}"
1963
1964    def merge_sql(self, expression: exp.Merge) -> str:
1965        this = self.sql(expression, "this")
1966        using = f"USING {self.sql(expression, 'using')}"
1967        on = f"ON {self.sql(expression, 'on')}"
1968        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"

Generator interprets the given syntax tree and produces a SQL string as an output.

Arguments:
  • time_mapping (dict): the dictionary of custom time mappings in which the key represents a python time format and the output the target time format
  • time_trie (trie): a trie of the time_mapping keys
  • pretty (bool): if set to True the returned string will be formatted. Default: False.
  • quote_start (str): specifies which starting character to use to delimit quotes. Default: '.
  • quote_end (str): specifies which ending character to use to delimit quotes. Default: '.
  • identifier_start (str): specifies which starting character to use to delimit identifiers. Default: ".
  • identifier_end (str): specifies which ending character to use to delimit identifiers. Default: ".
  • identify (bool): if set to True all identifiers will be delimited by the corresponding character.
  • normalize (bool): if set to True all identifiers will lower cased
  • string_escape (str): specifies a string escape character. Default: '.
  • identifier_escape (str): specifies an identifier escape character. Default: ".
  • pad (int): determines padding in a formatted string. Default: 2.
  • indent (int): determines the size of indentation in a formatted string. Default: 4.
  • unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
  • normalize_functions (str): normalize function names, "upper", "lower", or None Default: "upper"
  • alias_post_tablesample (bool): if the table alias comes after tablesample Default: False
  • unsupported_level (ErrorLevel): determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
  • null_ordering (str): Indicates the default null ordering method to use if not explicitly set. Options are "nulls_are_small", "nulls_are_large", "nulls_are_last". Default: "nulls_are_small"
  • max_unsupported (int): 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 (bool): if the the comma is leading or trailing in select statements 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( time_mapping=None, time_trie=None, pretty=None, quote_start=None, quote_end=None, identifier_start=None, identifier_end=None, identify=False, normalize=False, string_escape=None, identifier_escape=None, pad=2, indent=2, index_offset=0, unnest_column_only=False, alias_post_tablesample=False, normalize_functions='upper', unsupported_level=<ErrorLevel.WARN: 'WARN'>, null_ordering=None, max_unsupported=3, leading_comma=False, max_text_width=80, comments=True)
214    def __init__(
215        self,
216        time_mapping=None,
217        time_trie=None,
218        pretty=None,
219        quote_start=None,
220        quote_end=None,
221        identifier_start=None,
222        identifier_end=None,
223        identify=False,
224        normalize=False,
225        string_escape=None,
226        identifier_escape=None,
227        pad=2,
228        indent=2,
229        index_offset=0,
230        unnest_column_only=False,
231        alias_post_tablesample=False,
232        normalize_functions="upper",
233        unsupported_level=ErrorLevel.WARN,
234        null_ordering=None,
235        max_unsupported=3,
236        leading_comma=False,
237        max_text_width=80,
238        comments=True,
239    ):
240        import sqlglot
241
242        self.time_mapping = time_mapping or {}
243        self.time_trie = time_trie
244        self.pretty = pretty if pretty is not None else sqlglot.pretty
245        self.quote_start = quote_start or "'"
246        self.quote_end = quote_end or "'"
247        self.identifier_start = identifier_start or '"'
248        self.identifier_end = identifier_end or '"'
249        self.identify = identify
250        self.normalize = normalize
251        self.string_escape = string_escape or "'"
252        self.identifier_escape = identifier_escape or '"'
253        self.pad = pad
254        self.index_offset = index_offset
255        self.unnest_column_only = unnest_column_only
256        self.alias_post_tablesample = alias_post_tablesample
257        self.normalize_functions = normalize_functions
258        self.unsupported_level = unsupported_level
259        self.unsupported_messages = []
260        self.max_unsupported = max_unsupported
261        self.null_ordering = null_ordering
262        self._indent = indent
263        self._escaped_quote_end = self.string_escape + self.quote_end
264        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
265        self._leading_comma = leading_comma
266        self._max_text_width = max_text_width
267        self._comments = comments
def generate(self, expression: Optional[sqlglot.expressions.Expression]) -> str:
269    def generate(self, expression: t.Optional[exp.Expression]) -> str:
270        """
271        Generates a SQL string by interpreting the given syntax tree.
272
273        Args
274            expression: the syntax tree.
275
276        Returns
277            the SQL string.
278        """
279        self.unsupported_messages = []
280        sql = self.sql(expression).strip()
281
282        if self.unsupported_level == ErrorLevel.IGNORE:
283            return sql
284
285        if self.unsupported_level == ErrorLevel.WARN:
286            for msg in self.unsupported_messages:
287                logger.warning(msg)
288        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
289            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
290
291        if self.pretty:
292            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
293        return sql

Generates a SQL string by interpreting the given syntax tree.

Args expression: the syntax tree.

Returns the SQL string.

def unsupported(self, message: str) -> None:
295    def unsupported(self, message: str) -> None:
296        if self.unsupported_level == ErrorLevel.IMMEDIATE:
297            raise UnsupportedError(message)
298        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
300    def sep(self, sep: str = " ") -> str:
301        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
303    def seg(self, sql: str, sep: str = " ") -> str:
304        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
306    def pad_comment(self, comment: str) -> str:
307        comment = " " + comment if comment[0].strip() else comment
308        comment = comment + " " if comment[-1].strip() else comment
309        return comment
def maybe_comment(self, sql: str, expression: sqlglot.expressions.Expression) -> str:
311    def maybe_comment(self, sql: str, expression: exp.Expression) -> str:
312        comments = expression.comments if self._comments else None
313
314        if not comments:
315            return sql
316
317        sep = "\n" if self.pretty else " "
318        comments_sql = sep.join(
319            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
320        )
321
322        if not comments_sql:
323            return sql
324
325        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
326            return f"{comments_sql}{self.sep()}{sql}"
327
328        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
330    def wrap(self, expression: exp.Expression | str) -> str:
331        this_sql = self.indent(
332            self.sql(expression)
333            if isinstance(expression, (exp.Select, exp.Union))
334            else self.sql(expression, "this"),
335            level=1,
336            pad=0,
337        )
338        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
340    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
341        original = self.identify
342        self.identify = False
343        result = func(*args, **kwargs)
344        self.identify = original
345        return result
def normalize_func(self, name: str) -> str:
347    def normalize_func(self, name: str) -> str:
348        if self.normalize_functions == "upper":
349            return name.upper()
350        if self.normalize_functions == "lower":
351            return name.lower()
352        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
354    def indent(
355        self,
356        sql: str,
357        level: int = 0,
358        pad: t.Optional[int] = None,
359        skip_first: bool = False,
360        skip_last: bool = False,
361    ) -> str:
362        if not self.pretty:
363            return sql
364
365        pad = self.pad if pad is None else pad
366        lines = sql.split("\n")
367
368        return "\n".join(
369            line
370            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
371            else f"{' ' * (level * self._indent + pad)}{line}"
372            for i, line in enumerate(lines)
373        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
375    def sql(
376        self,
377        expression: t.Optional[str | exp.Expression],
378        key: t.Optional[str] = None,
379        comment: bool = True,
380    ) -> str:
381        if not expression:
382            return ""
383
384        if isinstance(expression, str):
385            return expression
386
387        if key:
388            return self.sql(expression.args.get(key))
389
390        transform = self.TRANSFORMS.get(expression.__class__)
391
392        if callable(transform):
393            sql = transform(self, expression)
394        elif transform:
395            sql = transform
396        elif isinstance(expression, exp.Expression):
397            exp_handler_name = f"{expression.key}_sql"
398
399            if hasattr(self, exp_handler_name):
400                sql = getattr(self, exp_handler_name)(expression)
401            elif isinstance(expression, exp.Func):
402                sql = self.function_fallback_sql(expression)
403            elif isinstance(expression, exp.Property):
404                sql = self.property_sql(expression)
405            else:
406                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
407        else:
408            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
409
410        return self.maybe_comment(sql, expression) if self._comments and comment else sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
412    def uncache_sql(self, expression: exp.Uncache) -> str:
413        table = self.sql(expression, "this")
414        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
415        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
417    def cache_sql(self, expression: exp.Cache) -> str:
418        lazy = " LAZY" if expression.args.get("lazy") else ""
419        table = self.sql(expression, "this")
420        options = expression.args.get("options")
421        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
422        sql = self.sql(expression, "expression")
423        sql = f" AS{self.sep()}{sql}" if sql else ""
424        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
425        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
427    def characterset_sql(self, expression: exp.CharacterSet) -> str:
428        if isinstance(expression.parent, exp.Cast):
429            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
430        default = "DEFAULT " if expression.args.get("default") else ""
431        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
433    def column_sql(self, expression: exp.Column) -> str:
434        return ".".join(
435            self.sql(part)
436            for part in (
437                expression.args.get("catalog"),
438                expression.args.get("db"),
439                expression.args.get("table"),
440                expression.args.get("this"),
441            )
442            if part
443        )
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef) -> str:
445    def columndef_sql(self, expression: exp.ColumnDef) -> str:
446        column = self.sql(expression, "this")
447        kind = self.sql(expression, "kind")
448        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
449        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
450        kind = f" {kind}" if kind else ""
451        constraints = f" {constraints}" if constraints else ""
452
453        return f"{exists}{column}{kind}{constraints}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
455    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
456        this = self.sql(expression, "this")
457        kind_sql = self.sql(expression, "kind")
458        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def autoincrementcolumnconstraint_sql(self, _) -> str:
460    def autoincrementcolumnconstraint_sql(self, _) -> str:
461        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
463    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
464        if isinstance(expression.this, list):
465            this = self.wrap(self.expressions(expression, key="this", flat=True))
466        else:
467            this = self.sql(expression, "this")
468
469        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
471    def generatedasidentitycolumnconstraint_sql(
472        self, expression: exp.GeneratedAsIdentityColumnConstraint
473    ) -> str:
474        this = ""
475        if expression.this is not None:
476            this = " ALWAYS " if expression.this else " BY DEFAULT "
477        start = expression.args.get("start")
478        start = f"START WITH {start}" if start else ""
479        increment = expression.args.get("increment")
480        increment = f" INCREMENT BY {increment}" if increment else ""
481        minvalue = expression.args.get("minvalue")
482        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
483        maxvalue = expression.args.get("maxvalue")
484        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
485        cycle = expression.args.get("cycle")
486        cycle_sql = ""
487        if cycle is not None:
488            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
489            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
490        sequence_opts = ""
491        if start or increment or cycle_sql:
492            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
493            sequence_opts = f" ({sequence_opts.strip()})"
494        return f"GENERATED{this}AS IDENTITY{sequence_opts}"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
496    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
497        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
499    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
500        desc = expression.args.get("desc")
501        if desc is not None:
502            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
503        return f"PRIMARY KEY"
def uniquecolumnconstraint_sql(self, _) -> str:
505    def uniquecolumnconstraint_sql(self, _) -> str:
506        return "UNIQUE"
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
508    def create_sql(self, expression: exp.Create) -> str:
509        kind = self.sql(expression, "kind").upper()
510        properties = expression.args.get("properties")
511        properties_exp = expression.copy()
512        properties_locs = self.locate_properties(properties) if properties else {}
513        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
514            exp.Properties.Location.POST_WITH
515        ):
516            properties_exp.set(
517                "properties",
518                exp.Properties(
519                    expressions=[
520                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
521                        *properties_locs[exp.Properties.Location.POST_WITH],
522                    ]
523                ),
524            )
525        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
526            this_name = self.sql(expression.this, "this")
527            this_properties = self.properties(
528                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
529                wrapped=False,
530            )
531            this_schema = f"({self.expressions(expression.this)})"
532            this = f"{this_name}, {this_properties} {this_schema}"
533            properties_sql = ""
534        else:
535            this = self.sql(expression, "this")
536            properties_sql = self.sql(properties_exp, "properties")
537        begin = " BEGIN" if expression.args.get("begin") else ""
538        expression_sql = self.sql(expression, "expression")
539        if expression_sql:
540            expression_sql = f"{begin}{self.sep()}{expression_sql}"
541
542            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
543                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
544                    postalias_props_sql = self.properties(
545                        exp.Properties(
546                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
547                        ),
548                        wrapped=False,
549                    )
550                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
551                else:
552                    expression_sql = f" AS{expression_sql}"
553
554        postindex_props_sql = ""
555        if properties_locs.get(exp.Properties.Location.POST_INDEX):
556            postindex_props_sql = self.properties(
557                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
558                wrapped=False,
559                prefix=" ",
560            )
561
562        indexes = expression.args.get("indexes")
563        if indexes:
564            indexes_sql: t.List[str] = []
565            for index in indexes:
566                ind_unique = " UNIQUE" if index.args.get("unique") else ""
567                ind_primary = " PRIMARY" if index.args.get("primary") else ""
568                ind_amp = " AMP" if index.args.get("amp") else ""
569                ind_name = f" {index.name}" if index.name else ""
570                ind_columns = (
571                    f' ({self.expressions(index, key="columns", flat=True)})'
572                    if index.args.get("columns")
573                    else ""
574                )
575                ind_sql = f"{ind_unique}{ind_primary}{ind_amp} INDEX{ind_name}{ind_columns}"
576
577                if indexes_sql:
578                    indexes_sql.append(ind_sql)
579                else:
580                    indexes_sql.append(
581                        f"{ind_sql}{postindex_props_sql}"
582                        if index.args.get("primary")
583                        else f"{postindex_props_sql}{ind_sql}"
584                    )
585
586            index_sql = "".join(indexes_sql)
587        else:
588            index_sql = postindex_props_sql
589
590        replace = " OR REPLACE" if expression.args.get("replace") else ""
591        unique = " UNIQUE" if expression.args.get("unique") else ""
592        volatile = " VOLATILE" if expression.args.get("volatile") else ""
593
594        postcreate_props_sql = ""
595        if properties_locs.get(exp.Properties.Location.POST_CREATE):
596            postcreate_props_sql = self.properties(
597                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
598                sep=" ",
599                prefix=" ",
600                wrapped=False,
601            )
602
603        modifiers = "".join((replace, unique, volatile, postcreate_props_sql))
604
605        postexpression_props_sql = ""
606        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
607            postexpression_props_sql = self.properties(
608                exp.Properties(
609                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
610                ),
611                sep=" ",
612                prefix=" ",
613                wrapped=False,
614            )
615
616        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
617        no_schema_binding = (
618            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
619        )
620
621        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}"
622        return self.prepend_ctes(expression, expression_sql)
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
624    def describe_sql(self, expression: exp.Describe) -> str:
625        return f"DESCRIBE {self.sql(expression, 'this')}"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
627    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
628        with_ = self.sql(expression, "with")
629        if with_:
630            sql = f"{with_}{self.sep()}{sql}"
631        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
633    def with_sql(self, expression: exp.With) -> str:
634        sql = self.expressions(expression, flat=True)
635        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
636
637        return f"WITH {recursive}{sql}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
639    def cte_sql(self, expression: exp.CTE) -> str:
640        alias = self.sql(expression, "alias")
641        return f"{alias} AS {self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
643    def tablealias_sql(self, expression: exp.TableAlias) -> str:
644        alias = self.sql(expression, "this")
645        columns = self.expressions(expression, key="columns", flat=True)
646        columns = f"({columns})" if columns else ""
647        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
649    def bitstring_sql(self, expression: exp.BitString) -> str:
650        return self.sql(expression, "this")
def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str:
652    def hexstring_sql(self, expression: exp.HexString) -> str:
653        return self.sql(expression, "this")
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
655    def datatype_sql(self, expression: exp.DataType) -> str:
656        type_value = expression.this
657        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
658        nested = ""
659        interior = self.expressions(expression, flat=True)
660        values = ""
661        if interior:
662            if expression.args.get("nested"):
663                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
664                if expression.args.get("values") is not None:
665                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
666                    values = (
667                        f"{delimiters[0]}{self.expressions(expression, 'values')}{delimiters[1]}"
668                    )
669            else:
670                nested = f"({interior})"
671
672        return f"{type_sql}{nested}{values}"
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
674    def directory_sql(self, expression: exp.Directory) -> str:
675        local = "LOCAL " if expression.args.get("local") else ""
676        row_format = self.sql(expression, "row_format")
677        row_format = f" {row_format}" if row_format else ""
678        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
680    def delete_sql(self, expression: exp.Delete) -> str:
681        this = self.sql(expression, "this")
682        this = f" FROM {this}" if this else ""
683        using_sql = (
684            f" USING {self.expressions(expression, 'using', sep=', USING ')}"
685            if expression.args.get("using")
686            else ""
687        )
688        where_sql = self.sql(expression, "where")
689        sql = f"DELETE{this}{using_sql}{where_sql}"
690        return self.prepend_ctes(expression, sql)
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
692    def drop_sql(self, expression: exp.Drop) -> str:
693        this = self.sql(expression, "this")
694        kind = expression.args["kind"]
695        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
696        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
697        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
698        cascade = " CASCADE" if expression.args.get("cascade") else ""
699        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}"
def except_sql(self, expression: sqlglot.expressions.Except) -> str:
701    def except_sql(self, expression: exp.Except) -> str:
702        return self.prepend_ctes(
703            expression,
704            self.set_operation(expression, self.except_op(expression)),
705        )
def except_op(self, expression: sqlglot.expressions.Except) -> str:
707    def except_op(self, expression: exp.Except) -> str:
708        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
710    def fetch_sql(self, expression: exp.Fetch) -> str:
711        direction = expression.args.get("direction")
712        direction = f" {direction.upper()}" if direction else ""
713        count = expression.args.get("count")
714        count = f" {count}" if count else ""
715        return f"{self.seg('FETCH')}{direction}{count} ROWS ONLY"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
717    def filter_sql(self, expression: exp.Filter) -> str:
718        this = self.sql(expression, "this")
719        where = self.sql(expression, "expression")[1:]  # where has a leading space
720        return f"{this} FILTER({where})"
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
722    def hint_sql(self, expression: exp.Hint) -> str:
723        if self.sql(expression, "this"):
724            self.unsupported("Hints are not supported")
725        return ""
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
727    def index_sql(self, expression: exp.Index) -> str:
728        this = self.sql(expression, "this")
729        table = self.sql(expression, "table")
730        columns = self.sql(expression, "columns")
731        return f"{this} ON {table} {columns}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
733    def identifier_sql(self, expression: exp.Identifier) -> str:
734        text = expression.name
735        text = text.lower() if self.normalize else text
736        text = text.replace(self.identifier_end, self._escaped_identifier_end)
737        if expression.args.get("quoted") or self.identify:
738            text = f"{self.identifier_start}{text}{self.identifier_end}"
739        return text
def national_sql(self, expression: sqlglot.expressions.National) -> str:
741    def national_sql(self, expression: exp.National) -> str:
742        return f"N{self.sql(expression, 'this')}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
744    def partition_sql(self, expression: exp.Partition) -> str:
745        return f"PARTITION({self.expressions(expression)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
747    def properties_sql(self, expression: exp.Properties) -> str:
748        root_properties = []
749        with_properties = []
750
751        for p in expression.expressions:
752            p_loc = self.PROPERTIES_LOCATION[p.__class__]
753            if p_loc == exp.Properties.Location.POST_WITH:
754                with_properties.append(p)
755            elif p_loc == exp.Properties.Location.POST_SCHEMA:
756                root_properties.append(p)
757
758        return self.root_properties(
759            exp.Properties(expressions=root_properties)
760        ) + self.with_properties(exp.Properties(expressions=with_properties))
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
762    def root_properties(self, properties: exp.Properties) -> str:
763        if properties.expressions:
764            return self.sep() + self.expressions(properties, indent=False, sep=" ")
765        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
767    def properties(
768        self,
769        properties: exp.Properties,
770        prefix: str = "",
771        sep: str = ", ",
772        suffix: str = "",
773        wrapped: bool = True,
774    ) -> str:
775        if properties.expressions:
776            expressions = self.expressions(properties, sep=sep, indent=False)
777            expressions = self.wrap(expressions) if wrapped else expressions
778            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
779        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
781    def with_properties(self, properties: exp.Properties) -> str:
782        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]]:
784    def locate_properties(
785        self, properties: exp.Properties
786    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
787        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
788            key: [] for key in exp.Properties.Location
789        }
790
791        for p in properties.expressions:
792            p_loc = self.PROPERTIES_LOCATION[p.__class__]
793            if p_loc == exp.Properties.Location.POST_NAME:
794                properties_locs[exp.Properties.Location.POST_NAME].append(p)
795            elif p_loc == exp.Properties.Location.POST_INDEX:
796                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
797            elif p_loc == exp.Properties.Location.POST_SCHEMA:
798                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
799            elif p_loc == exp.Properties.Location.POST_WITH:
800                properties_locs[exp.Properties.Location.POST_WITH].append(p)
801            elif p_loc == exp.Properties.Location.POST_CREATE:
802                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
803            elif p_loc == exp.Properties.Location.POST_ALIAS:
804                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
805            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
806                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
807            elif p_loc == exp.Properties.Location.UNSUPPORTED:
808                self.unsupported(f"Unsupported property {p.key}")
809
810        return properties_locs
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
812    def property_sql(self, expression: exp.Property) -> str:
813        property_cls = expression.__class__
814        if property_cls == exp.Property:
815            return f"{expression.name}={self.sql(expression, 'value')}"
816
817        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
818        if not property_name:
819            self.unsupported(f"Unsupported property {expression.key}")
820
821        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
823    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
824        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
825        options = f" {options}" if options else ""
826        return f"LIKE {self.sql(expression, 'this')}{options}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
828    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
829        no = "NO " if expression.args.get("no") else ""
830        protection = " PROTECTION" if expression.args.get("protection") else ""
831        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
833    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
834        no = "NO " if expression.args.get("no") else ""
835        dual = "DUAL " if expression.args.get("dual") else ""
836        before = "BEFORE " if expression.args.get("before") else ""
837        return f"{no}{dual}{before}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
839    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
840        freespace = self.sql(expression, "this")
841        percent = " PERCENT" if expression.args.get("percent") else ""
842        return f"FREESPACE={freespace}{percent}"
def afterjournalproperty_sql(self, expression: sqlglot.expressions.AfterJournalProperty) -> str:
844    def afterjournalproperty_sql(self, expression: exp.AfterJournalProperty) -> str:
845        no = "NO " if expression.args.get("no") else ""
846        dual = "DUAL " if expression.args.get("dual") else ""
847        local = ""
848        if expression.args.get("local") is not None:
849            local = "LOCAL " if expression.args.get("local") else "NOT LOCAL "
850        return f"{no}{dual}{local}AFTER JOURNAL"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
852    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
853        if expression.args.get("default"):
854            property = "DEFAULT"
855        elif expression.args.get("on"):
856            property = "ON"
857        else:
858            property = "OFF"
859        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
861    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
862        if expression.args.get("no"):
863            return "NO MERGEBLOCKRATIO"
864        if expression.args.get("default"):
865            return "DEFAULT MERGEBLOCKRATIO"
866
867        percent = " PERCENT" if expression.args.get("percent") else ""
868        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
870    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
871        default = expression.args.get("default")
872        min = expression.args.get("min")
873        if default is not None or min is not None:
874            if default:
875                property = "DEFAULT"
876            elif min:
877                property = "MINIMUM"
878            else:
879                property = "MAXIMUM"
880            return f"{property} DATABLOCKSIZE"
881        else:
882            units = expression.args.get("units")
883            units = f" {units}" if units else ""
884            return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
886    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
887        autotemp = expression.args.get("autotemp")
888        always = expression.args.get("always")
889        default = expression.args.get("default")
890        manual = expression.args.get("manual")
891        never = expression.args.get("never")
892
893        if autotemp is not None:
894            property = f"AUTOTEMP({self.expressions(autotemp)})"
895        elif always:
896            property = "ALWAYS"
897        elif default:
898            property = "DEFAULT"
899        elif manual:
900            property = "MANUAL"
901        elif never:
902            property = "NEVER"
903        return f"BLOCKCOMPRESSION={property}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
905    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
906        no = expression.args.get("no")
907        no = " NO" if no else ""
908        concurrent = expression.args.get("concurrent")
909        concurrent = " CONCURRENT" if concurrent else ""
910
911        for_ = ""
912        if expression.args.get("for_all"):
913            for_ = " FOR ALL"
914        elif expression.args.get("for_insert"):
915            for_ = " FOR INSERT"
916        elif expression.args.get("for_none"):
917            for_ = " FOR NONE"
918        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
920    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
921        kind = expression.args.get("kind")
922        this: str = f" {this}" if expression.this else ""
923        for_or_in = expression.args.get("for_or_in")
924        lock_type = expression.args.get("lock_type")
925        override = " OVERRIDE" if expression.args.get("override") else ""
926        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
928    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
929        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
930        statistics = expression.args.get("statistics")
931        statistics_sql = ""
932        if statistics is not None:
933            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
934        return f"{data_sql}{statistics_sql}"
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
936    def insert_sql(self, expression: exp.Insert) -> str:
937        overwrite = expression.args.get("overwrite")
938
939        if isinstance(expression.this, exp.Directory):
940            this = "OVERWRITE " if overwrite else "INTO "
941        else:
942            this = "OVERWRITE TABLE " if overwrite else "INTO "
943
944        alternative = expression.args.get("alternative")
945        alternative = f" OR {alternative} " if alternative else " "
946        this = f"{this}{self.sql(expression, 'this')}"
947
948        exists = " IF EXISTS " if expression.args.get("exists") else " "
949        partition_sql = (
950            self.sql(expression, "partition") if expression.args.get("partition") else ""
951        )
952        expression_sql = self.sql(expression, "expression")
953        sep = self.sep() if partition_sql else ""
954        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}"
955        return self.prepend_ctes(expression, sql)
def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str:
957    def intersect_sql(self, expression: exp.Intersect) -> str:
958        return self.prepend_ctes(
959            expression,
960            self.set_operation(expression, self.intersect_op(expression)),
961        )
def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str:
963    def intersect_op(self, expression: exp.Intersect) -> str:
964        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
966    def introducer_sql(self, expression: exp.Introducer) -> str:
967        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
969    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
970        return expression.name.upper()
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
972    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
973        fields = expression.args.get("fields")
974        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
975        escaped = expression.args.get("escaped")
976        escaped = f" ESCAPED BY {escaped}" if escaped else ""
977        items = expression.args.get("collection_items")
978        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
979        keys = expression.args.get("map_keys")
980        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
981        lines = expression.args.get("lines")
982        lines = f" LINES TERMINATED BY {lines}" if lines else ""
983        null = expression.args.get("null")
984        null = f" NULL DEFINED AS {null}" if null else ""
985        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
 987    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
 988        table = ".".join(
 989            part
 990            for part in [
 991                self.sql(expression, "catalog"),
 992                self.sql(expression, "db"),
 993                self.sql(expression, "this"),
 994            ]
 995            if part
 996        )
 997
 998        alias = self.sql(expression, "alias")
 999        alias = f"{sep}{alias}" if alias else ""
1000        hints = self.expressions(expression, key="hints", sep=", ", flat=True)
1001        hints = f" WITH ({hints})" if hints else ""
1002        laterals = self.expressions(expression, key="laterals", sep="")
1003        joins = self.expressions(expression, key="joins", sep="")
1004        pivots = self.expressions(expression, key="pivots", sep="")
1005        system_time = expression.args.get("system_time")
1006        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
1007
1008        return f"{table}{system_time}{alias}{hints}{laterals}{joins}{pivots}"
def tablesample_sql(self, expression: sqlglot.expressions.TableSample) -> str:
1010    def tablesample_sql(self, expression: exp.TableSample) -> str:
1011        if self.alias_post_tablesample and expression.this.alias:
1012            this = self.sql(expression.this, "this")
1013            alias = f" AS {self.sql(expression.this, 'alias')}"
1014        else:
1015            this = self.sql(expression, "this")
1016            alias = ""
1017        method = self.sql(expression, "method")
1018        method = f" {method.upper()} " if method else ""
1019        numerator = self.sql(expression, "bucket_numerator")
1020        denominator = self.sql(expression, "bucket_denominator")
1021        field = self.sql(expression, "bucket_field")
1022        field = f" ON {field}" if field else ""
1023        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1024        percent = self.sql(expression, "percent")
1025        percent = f"{percent} PERCENT" if percent else ""
1026        rows = self.sql(expression, "rows")
1027        rows = f"{rows} ROWS" if rows else ""
1028        size = self.sql(expression, "size")
1029        seed = self.sql(expression, "seed")
1030        seed = f" SEED ({seed})" if seed else ""
1031        return f"{this} TABLESAMPLE{method}({bucket}{percent}{rows}{size}){seed}{alias}"
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
1033    def pivot_sql(self, expression: exp.Pivot) -> str:
1034        this = self.sql(expression, "this")
1035        alias = self.sql(expression, "alias")
1036        alias = f" AS {alias}" if alias else ""
1037        unpivot = expression.args.get("unpivot")
1038        direction = "UNPIVOT" if unpivot else "PIVOT"
1039        expressions = self.expressions(expression, key="expressions")
1040        field = self.sql(expression, "field")
1041        return f"{this} {direction}({expressions} FOR {field}){alias}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
1043    def tuple_sql(self, expression: exp.Tuple) -> str:
1044        return f"({self.expressions(expression, flat=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
1046    def update_sql(self, expression: exp.Update) -> str:
1047        this = self.sql(expression, "this")
1048        set_sql = self.expressions(expression, flat=True)
1049        from_sql = self.sql(expression, "from")
1050        where_sql = self.sql(expression, "where")
1051        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}"
1052        return self.prepend_ctes(expression, sql)
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
1054    def values_sql(self, expression: exp.Values) -> str:
1055        args = self.expressions(expression)
1056        alias = self.sql(expression, "alias")
1057        values = f"VALUES{self.seg('')}{args}"
1058        values = (
1059            f"({values})"
1060            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1061            else values
1062        )
1063        return f"{values} AS {alias}" if alias else values
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
1065    def var_sql(self, expression: exp.Var) -> str:
1066        return self.sql(expression, "this")
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
1068    def into_sql(self, expression: exp.Into) -> str:
1069        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1070        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1071        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
1073    def from_sql(self, expression: exp.From) -> str:
1074        expressions = self.expressions(expression, flat=True)
1075        return f"{self.seg('FROM')} {expressions}"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
1077    def group_sql(self, expression: exp.Group) -> str:
1078        group_by = self.op_expressions("GROUP BY", expression)
1079        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1080        grouping_sets = (
1081            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1082        )
1083
1084        cube = expression.args.get("cube", [])
1085        if seq_get(cube, 0) is True:
1086            return f"{group_by}{self.seg('WITH CUBE')}"
1087        else:
1088            cube_sql = self.expressions(expression, key="cube", indent=False)
1089            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1090
1091        rollup = expression.args.get("rollup", [])
1092        if seq_get(rollup, 0) is True:
1093            return f"{group_by}{self.seg('WITH ROLLUP')}"
1094        else:
1095            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1096            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1097
1098        groupings = csv(grouping_sets, cube_sql, rollup_sql, sep=",")
1099
1100        if expression.args.get("expressions") and groupings:
1101            group_by = f"{group_by},"
1102
1103        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
1105    def having_sql(self, expression: exp.Having) -> str:
1106        this = self.indent(self.sql(expression, "this"))
1107        return f"{self.seg('HAVING')}{self.sep()}{this}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
1109    def join_sql(self, expression: exp.Join) -> str:
1110        op_sql = self.seg(
1111            " ".join(
1112                op
1113                for op in (
1114                    "NATURAL" if expression.args.get("natural") else None,
1115                    expression.side,
1116                    expression.kind,
1117                    "JOIN",
1118                )
1119                if op
1120            )
1121        )
1122        on_sql = self.sql(expression, "on")
1123        using = expression.args.get("using")
1124
1125        if not on_sql and using:
1126            on_sql = csv(*(self.sql(column) for column in using))
1127
1128        if on_sql:
1129            on_sql = self.indent(on_sql, skip_first=True)
1130            space = self.seg(" " * self.pad) if self.pretty else " "
1131            if using:
1132                on_sql = f"{space}USING ({on_sql})"
1133            else:
1134                on_sql = f"{space}ON {on_sql}"
1135
1136        expression_sql = self.sql(expression, "expression")
1137        this_sql = self.sql(expression, "this")
1138        return f"{expression_sql}{op_sql} {this_sql}{on_sql}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str:
1140    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1141        args = self.expressions(expression, flat=True)
1142        args = f"({args})" if len(args.split(",")) > 1 else args
1143        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
1145    def lateral_sql(self, expression: exp.Lateral) -> str:
1146        this = self.sql(expression, "this")
1147
1148        if isinstance(expression.this, exp.Subquery):
1149            return f"LATERAL {this}"
1150
1151        if expression.args.get("view"):
1152            alias = expression.args["alias"]
1153            columns = self.expressions(alias, key="columns", flat=True)
1154            table = f" {alias.name}" if alias.name else ""
1155            columns = f" AS {columns}" if columns else ""
1156            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1157            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1158
1159        alias = self.sql(expression, "alias")
1160        alias = f" AS {alias}" if alias else ""
1161        return f"LATERAL {this}{alias}"
def limit_sql(self, expression: sqlglot.expressions.Limit) -> str:
1163    def limit_sql(self, expression: exp.Limit) -> str:
1164        this = self.sql(expression, "this")
1165        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
1167    def offset_sql(self, expression: exp.Offset) -> str:
1168        this = self.sql(expression, "this")
1169        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
1171    def lock_sql(self, expression: exp.Lock) -> str:
1172        if self.LOCKING_READS_SUPPORTED:
1173            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
1174            return self.seg(f"FOR {lock_type}")
1175
1176        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1177        return ""
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
1179    def literal_sql(self, expression: exp.Literal) -> str:
1180        text = expression.this or ""
1181        if expression.is_string:
1182            text = text.replace(self.quote_end, self._escaped_quote_end)
1183            if self.pretty:
1184                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1185            text = f"{self.quote_start}{text}{self.quote_end}"
1186        return text
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
1188    def loaddata_sql(self, expression: exp.LoadData) -> str:
1189        local = " LOCAL" if expression.args.get("local") else ""
1190        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1191        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1192        this = f" INTO TABLE {self.sql(expression, 'this')}"
1193        partition = self.sql(expression, "partition")
1194        partition = f" {partition}" if partition else ""
1195        input_format = self.sql(expression, "input_format")
1196        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1197        serde = self.sql(expression, "serde")
1198        serde = f" SERDE {serde}" if serde else ""
1199        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
1201    def null_sql(self, *_) -> str:
1202        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
1204    def boolean_sql(self, expression: exp.Boolean) -> str:
1205        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
1207    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1208        this = self.sql(expression, "this")
1209        this = f"{this} " if this else this
1210        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
1212    def cluster_sql(self, expression: exp.Cluster) -> str:
1213        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
1215    def distribute_sql(self, expression: exp.Distribute) -> str:
1216        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
1218    def sort_sql(self, expression: exp.Sort) -> str:
1219        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
1221    def ordered_sql(self, expression: exp.Ordered) -> str:
1222        desc = expression.args.get("desc")
1223        asc = not desc
1224
1225        nulls_first = expression.args.get("nulls_first")
1226        nulls_last = not nulls_first
1227        nulls_are_large = self.null_ordering == "nulls_are_large"
1228        nulls_are_small = self.null_ordering == "nulls_are_small"
1229        nulls_are_last = self.null_ordering == "nulls_are_last"
1230
1231        sort_order = " DESC" if desc else ""
1232        nulls_sort_change = ""
1233        if nulls_first and (
1234            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
1235        ):
1236            nulls_sort_change = " NULLS FIRST"
1237        elif (
1238            nulls_last
1239            and ((asc and nulls_are_small) or (desc and nulls_are_large))
1240            and not nulls_are_last
1241        ):
1242            nulls_sort_change = " NULLS LAST"
1243
1244        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
1245            self.unsupported(
1246                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
1247            )
1248            nulls_sort_change = ""
1249
1250        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
1252    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
1253        partition = self.partition_by_sql(expression)
1254        order = self.sql(expression, "order")
1255        measures = self.sql(expression, "measures")
1256        measures = self.seg(f"MEASURES {measures}") if measures else ""
1257        rows = self.sql(expression, "rows")
1258        rows = self.seg(rows) if rows else ""
1259        after = self.sql(expression, "after")
1260        after = self.seg(after) if after else ""
1261        pattern = self.sql(expression, "pattern")
1262        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
1263        define = self.sql(expression, "define")
1264        define = self.seg(f"DEFINE {define}") if define else ""
1265        body = "".join(
1266            (
1267                partition,
1268                order,
1269                measures,
1270                rows,
1271                after,
1272                pattern,
1273                define,
1274            )
1275        )
1276        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
1278    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
1279        return csv(
1280            *sqls,
1281            *[self.sql(sql) for sql in expression.args.get("joins") or []],
1282            self.sql(expression, "match"),
1283            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
1284            self.sql(expression, "where"),
1285            self.sql(expression, "group"),
1286            self.sql(expression, "having"),
1287            self.sql(expression, "qualify"),
1288            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
1289            if expression.args.get("windows")
1290            else "",
1291            self.sql(expression, "distribute"),
1292            self.sql(expression, "sort"),
1293            self.sql(expression, "cluster"),
1294            self.sql(expression, "order"),
1295            self.sql(expression, "limit"),
1296            self.sql(expression, "offset"),
1297            self.sql(expression, "lock"),
1298            sep="",
1299        )
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
1301    def select_sql(self, expression: exp.Select) -> str:
1302        hint = self.sql(expression, "hint")
1303        distinct = self.sql(expression, "distinct")
1304        distinct = f" {distinct}" if distinct else ""
1305        expressions = self.expressions(expression)
1306        expressions = f"{self.sep()}{expressions}" if expressions else expressions
1307        sql = self.query_modifiers(
1308            expression,
1309            f"SELECT{hint}{distinct}{expressions}",
1310            self.sql(expression, "into", comment=False),
1311            self.sql(expression, "from", comment=False),
1312        )
1313        return self.prepend_ctes(expression, sql)
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
1315    def schema_sql(self, expression: exp.Schema) -> str:
1316        this = self.sql(expression, "this")
1317        this = f"{this} " if this else ""
1318        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
1319        return f"{this}{sql}"
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
1321    def star_sql(self, expression: exp.Star) -> str:
1322        except_ = self.expressions(expression, key="except", flat=True)
1323        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
1324        replace = self.expressions(expression, key="replace", flat=True)
1325        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
1326        return f"*{except_}{replace}"
def structkwarg_sql(self, expression: sqlglot.expressions.StructKwarg) -> str:
1328    def structkwarg_sql(self, expression: exp.StructKwarg) -> str:
1329        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
1331    def parameter_sql(self, expression: exp.Parameter) -> str:
1332        this = self.sql(expression, "this")
1333        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
1334        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
1336    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
1337        this = self.sql(expression, "this")
1338        kind = expression.text("kind")
1339        if kind:
1340            kind = f"{kind}."
1341        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
1343    def placeholder_sql(self, expression: exp.Placeholder) -> str:
1344        return f":{expression.name}" if expression.name else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
1346    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
1347        alias = self.sql(expression, "alias")
1348        alias = f"{sep}{alias}" if alias else ""
1349
1350        sql = self.query_modifiers(
1351            expression,
1352            self.wrap(expression),
1353            self.expressions(expression, key="pivots", sep=" "),
1354            alias,
1355        )
1356
1357        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
1359    def qualify_sql(self, expression: exp.Qualify) -> str:
1360        this = self.indent(self.sql(expression, "this"))
1361        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def union_sql(self, expression: sqlglot.expressions.Union) -> str:
1363    def union_sql(self, expression: exp.Union) -> str:
1364        return self.prepend_ctes(
1365            expression,
1366            self.set_operation(expression, self.union_op(expression)),
1367        )
def union_op(self, expression: sqlglot.expressions.Union) -> str:
1369    def union_op(self, expression: exp.Union) -> str:
1370        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
1371        kind = kind if expression.args.get("distinct") else " ALL"
1372        return f"UNION{kind}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
1374    def unnest_sql(self, expression: exp.Unnest) -> str:
1375        args = self.expressions(expression, flat=True)
1376        alias = expression.args.get("alias")
1377        if alias and self.unnest_column_only:
1378            columns = alias.columns
1379            alias = self.sql(columns[0]) if columns else ""
1380        else:
1381            alias = self.sql(expression, "alias")
1382        alias = f" AS {alias}" if alias else alias
1383        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
1384        offset = expression.args.get("offset")
1385        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
1386        return f"UNNEST({args}){ordinality}{alias}{offset}"
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
1388    def where_sql(self, expression: exp.Where) -> str:
1389        this = self.indent(self.sql(expression, "this"))
1390        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
1392    def window_sql(self, expression: exp.Window) -> str:
1393        this = self.sql(expression, "this")
1394
1395        partition = self.partition_by_sql(expression)
1396
1397        order = expression.args.get("order")
1398        order_sql = self.order_sql(order, flat=True) if order else ""
1399
1400        partition_sql = partition + " " if partition and order else partition
1401
1402        spec = expression.args.get("spec")
1403        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
1404
1405        alias = self.sql(expression, "alias")
1406        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
1407
1408        if not partition and not order and not spec and alias:
1409            return f"{this} {alias}"
1410
1411        window_args = alias + partition_sql + order_sql + spec_sql
1412
1413        return f"{this} ({window_args.strip()})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
1415    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
1416        partition = self.expressions(expression, key="partition_by", flat=True)
1417        return f"PARTITION BY {partition}" if partition else ""
def window_spec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
1419    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
1420        kind = self.sql(expression, "kind")
1421        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
1422        end = (
1423            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
1424            or "CURRENT ROW"
1425        )
1426        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
1428    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
1429        this = self.sql(expression, "this")
1430        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
1431        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
1433    def between_sql(self, expression: exp.Between) -> str:
1434        this = self.sql(expression, "this")
1435        low = self.sql(expression, "low")
1436        high = self.sql(expression, "high")
1437        return f"{this} BETWEEN {low} AND {high}"
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
1439    def bracket_sql(self, expression: exp.Bracket) -> str:
1440        expressions = apply_index_offset(expression.expressions, self.index_offset)
1441        expressions_sql = ", ".join(self.sql(e) for e in expressions)
1442
1443        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
1445    def all_sql(self, expression: exp.All) -> str:
1446        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
1448    def any_sql(self, expression: exp.Any) -> str:
1449        this = self.sql(expression, "this")
1450        if isinstance(expression.this, exp.Subqueryable):
1451            this = self.wrap(this)
1452        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
1454    def exists_sql(self, expression: exp.Exists) -> str:
1455        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
1457    def case_sql(self, expression: exp.Case) -> str:
1458        this = self.sql(expression, "this")
1459        statements = [f"CASE {this}" if this else "CASE"]
1460
1461        for e in expression.args["ifs"]:
1462            statements.append(f"WHEN {self.sql(e, 'this')}")
1463            statements.append(f"THEN {self.sql(e, 'true')}")
1464
1465        default = self.sql(expression, "default")
1466
1467        if default:
1468            statements.append(f"ELSE {default}")
1469
1470        statements.append("END")
1471
1472        if self.pretty and self.text_width(statements) > self._max_text_width:
1473            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
1474
1475        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
1477    def constraint_sql(self, expression: exp.Constraint) -> str:
1478        this = self.sql(expression, "this")
1479        expressions = self.expressions(expression, flat=True)
1480        return f"CONSTRAINT {this} {expressions}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
1482    def extract_sql(self, expression: exp.Extract) -> str:
1483        this = self.sql(expression, "this")
1484        expression_sql = self.sql(expression, "expression")
1485        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
1487    def trim_sql(self, expression: exp.Trim) -> str:
1488        trim_type = self.sql(expression, "position")
1489
1490        if trim_type == "LEADING":
1491            return self.func("LTRIM", expression.this)
1492        elif trim_type == "TRAILING":
1493            return self.func("RTRIM", expression.this)
1494        else:
1495            return self.func("TRIM", expression.this, expression.expression)
def concat_sql(self, expression: sqlglot.expressions.Concat) -> str:
1497    def concat_sql(self, expression: exp.Concat) -> str:
1498        if len(expression.expressions) == 1:
1499            return self.sql(expression.expressions[0])
1500        return self.function_fallback_sql(expression)
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
1502    def check_sql(self, expression: exp.Check) -> str:
1503        this = self.sql(expression, key="this")
1504        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
1506    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
1507        expressions = self.expressions(expression, flat=True)
1508        reference = self.sql(expression, "reference")
1509        reference = f" {reference}" if reference else ""
1510        delete = self.sql(expression, "delete")
1511        delete = f" ON DELETE {delete}" if delete else ""
1512        update = self.sql(expression, "update")
1513        update = f" ON UPDATE {update}" if update else ""
1514        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
1516    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
1517        expressions = self.expressions(expression, flat=True)
1518        options = self.expressions(expression, "options", flat=True, sep=" ")
1519        options = f" {options}" if options else ""
1520        return f"PRIMARY KEY ({expressions}){options}"
def unique_sql(self, expression: sqlglot.expressions.Unique) -> str:
1522    def unique_sql(self, expression: exp.Unique) -> str:
1523        columns = self.expressions(expression, key="expressions")
1524        return f"UNIQUE ({columns})"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
1526    def if_sql(self, expression: exp.If) -> str:
1527        return self.case_sql(
1528            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
1529        )
def in_sql(self, expression: sqlglot.expressions.In) -> str:
1531    def in_sql(self, expression: exp.In) -> str:
1532        query = expression.args.get("query")
1533        unnest = expression.args.get("unnest")
1534        field = expression.args.get("field")
1535        is_global = " GLOBAL" if expression.args.get("is_global") else ""
1536
1537        if query:
1538            in_sql = self.wrap(query)
1539        elif unnest:
1540            in_sql = self.in_unnest_op(unnest)
1541        elif field:
1542            in_sql = self.sql(field)
1543        else:
1544            in_sql = f"({self.expressions(expression, flat=True)})"
1545
1546        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
1548    def in_unnest_op(self, unnest: exp.Unnest) -> str:
1549        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
1551    def interval_sql(self, expression: exp.Interval) -> str:
1552        this = expression.args.get("this")
1553        if this:
1554            this = (
1555                f" {this}"
1556                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
1557                else f" ({this})"
1558            )
1559        else:
1560            this = ""
1561        unit = expression.args.get("unit")
1562        unit = f" {unit}" if unit else ""
1563        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
1565    def return_sql(self, expression: exp.Return) -> str:
1566        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
1568    def reference_sql(self, expression: exp.Reference) -> str:
1569        this = self.sql(expression, "this")
1570        expressions = self.expressions(expression, flat=True)
1571        expressions = f"({expressions})" if expressions else ""
1572        options = self.expressions(expression, "options", flat=True, sep=" ")
1573        options = f" {options}" if options else ""
1574        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
1576    def anonymous_sql(self, expression: exp.Anonymous) -> str:
1577        return self.func(expression.name, *expression.expressions)
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
1579    def paren_sql(self, expression: exp.Paren) -> str:
1580        if isinstance(expression.unnest(), exp.Select):
1581            sql = self.wrap(expression)
1582        else:
1583            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
1584            sql = f"({sql}{self.seg(')', sep='')}"
1585
1586        return self.prepend_ctes(expression, sql)
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
1588    def neg_sql(self, expression: exp.Neg) -> str:
1589        # This makes sure we don't convert "- - 5" to "--5", which is a comment
1590        this_sql = self.sql(expression, "this")
1591        sep = " " if this_sql[0] == "-" else ""
1592        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
1594    def not_sql(self, expression: exp.Not) -> str:
1595        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
1597    def alias_sql(self, expression: exp.Alias) -> str:
1598        to_sql = self.sql(expression, "alias")
1599        to_sql = f" AS {to_sql}" if to_sql else ""
1600        return f"{self.sql(expression, 'this')}{to_sql}"
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
1602    def aliases_sql(self, expression: exp.Aliases) -> str:
1603        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
1605    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
1606        this = self.sql(expression, "this")
1607        zone = self.sql(expression, "zone")
1608        return f"{this} AT TIME ZONE {zone}"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
1610    def add_sql(self, expression: exp.Add) -> str:
1611        return self.binary(expression, "+")
def and_sql(self, expression: sqlglot.expressions.And) -> str:
1613    def and_sql(self, expression: exp.And) -> str:
1614        return self.connector_sql(expression, "AND")
def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str:
1616    def connector_sql(self, expression: exp.Connector, op: str) -> str:
1617        if not self.pretty:
1618            return self.binary(expression, op)
1619
1620        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
1621        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
1622        return f"{sep}{op} ".join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
1624    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
1625        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
1627    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
1628        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
1630    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
1631        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
1633    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
1634        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
1636    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
1637        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
1639    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
1640        return self.binary(expression, "^")
def cast_sql(self, expression: sqlglot.expressions.Cast) -> str:
1642    def cast_sql(self, expression: exp.Cast) -> str:
1643        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
1645    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
1646        zone = self.sql(expression, "this")
1647        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
1649    def collate_sql(self, expression: exp.Collate) -> str:
1650        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
1652    def command_sql(self, expression: exp.Command) -> str:
1653        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
1655    def comment_sql(self, expression: exp.Comment) -> str:
1656        this = self.sql(expression, "this")
1657        kind = expression.args["kind"]
1658        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1659        expression_sql = self.sql(expression, "expression")
1660        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
def transaction_sql(self, *_) -> str:
1662    def transaction_sql(self, *_) -> str:
1663        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
1665    def commit_sql(self, expression: exp.Commit) -> str:
1666        chain = expression.args.get("chain")
1667        if chain is not None:
1668            chain = " AND CHAIN" if chain else " AND NO CHAIN"
1669
1670        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
1672    def rollback_sql(self, expression: exp.Rollback) -> str:
1673        savepoint = expression.args.get("savepoint")
1674        savepoint = f" TO {savepoint}" if savepoint else ""
1675        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
1677    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
1678        this = self.sql(expression, "this")
1679
1680        dtype = self.sql(expression, "dtype")
1681        if dtype:
1682            collate = self.sql(expression, "collate")
1683            collate = f" COLLATE {collate}" if collate else ""
1684            using = self.sql(expression, "using")
1685            using = f" USING {using}" if using else ""
1686            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
1687
1688        default = self.sql(expression, "default")
1689        if default:
1690            return f"ALTER COLUMN {this} SET DEFAULT {default}"
1691
1692        if not expression.args.get("drop"):
1693            self.unsupported("Unsupported ALTER COLUMN syntax")
1694
1695        return f"ALTER COLUMN {this} DROP DEFAULT"
def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str:
1697    def renametable_sql(self, expression: exp.RenameTable) -> str:
1698        this = self.sql(expression, "this")
1699        return f"RENAME TO {this}"
def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
1701    def altertable_sql(self, expression: exp.AlterTable) -> str:
1702        actions = expression.args["actions"]
1703
1704        if isinstance(actions[0], exp.ColumnDef):
1705            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
1706        elif isinstance(actions[0], exp.Schema):
1707            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
1708        elif isinstance(actions[0], exp.Delete):
1709            actions = self.expressions(expression, "actions", flat=True)
1710        else:
1711            actions = self.expressions(expression, "actions")
1712
1713        exists = " IF EXISTS" if expression.args.get("exists") else ""
1714        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
1716    def droppartition_sql(self, expression: exp.DropPartition) -> str:
1717        expressions = self.expressions(expression)
1718        exists = " IF EXISTS " if expression.args.get("exists") else " "
1719        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
1721    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
1722        this = self.sql(expression, "this")
1723        expression_ = self.sql(expression, "expression")
1724        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
1725
1726        enforced = expression.args.get("enforced")
1727        if enforced is not None:
1728            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
1729
1730        return f"{add_constraint} {expression_}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
1732    def distinct_sql(self, expression: exp.Distinct) -> str:
1733        this = self.expressions(expression, flat=True)
1734        this = f" {this}" if this else ""
1735
1736        on = self.sql(expression, "on")
1737        on = f" ON {on}" if on else ""
1738        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
1740    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
1741        return f"{self.sql(expression, 'this')} IGNORE NULLS"
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
1743    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
1744        return f"{self.sql(expression, 'this')} RESPECT NULLS"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
1746    def intdiv_sql(self, expression: exp.IntDiv) -> str:
1747        return self.sql(
1748            exp.Cast(
1749                this=exp.Div(this=expression.this, expression=expression.expression),
1750                to=exp.DataType(this=exp.DataType.Type.INT),
1751            )
1752        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
1754    def dpipe_sql(self, expression: exp.DPipe) -> str:
1755        return self.binary(expression, "||")
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
1757    def div_sql(self, expression: exp.Div) -> str:
1758        return self.binary(expression, "/")
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
1760    def overlaps_sql(self, expression: exp.Overlaps) -> str:
1761        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
1763    def distance_sql(self, expression: exp.Distance) -> str:
1764        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
1766    def dot_sql(self, expression: exp.Dot) -> str:
1767        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
1769    def eq_sql(self, expression: exp.EQ) -> str:
1770        return self.binary(expression, "=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
1772    def escape_sql(self, expression: exp.Escape) -> str:
1773        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
1775    def glob_sql(self, expression: exp.Glob) -> str:
1776        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
1778    def gt_sql(self, expression: exp.GT) -> str:
1779        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
1781    def gte_sql(self, expression: exp.GTE) -> str:
1782        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
1784    def ilike_sql(self, expression: exp.ILike) -> str:
1785        return self.binary(expression, "ILIKE")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
1787    def is_sql(self, expression: exp.Is) -> str:
1788        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
1790    def like_sql(self, expression: exp.Like) -> str:
1791        return self.binary(expression, "LIKE")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
1793    def similarto_sql(self, expression: exp.SimilarTo) -> str:
1794        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
1796    def lt_sql(self, expression: exp.LT) -> str:
1797        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
1799    def lte_sql(self, expression: exp.LTE) -> str:
1800        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
1802    def mod_sql(self, expression: exp.Mod) -> str:
1803        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
1805    def mul_sql(self, expression: exp.Mul) -> str:
1806        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
1808    def neq_sql(self, expression: exp.NEQ) -> str:
1809        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
1811    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
1812        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
1814    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
1815        return self.binary(expression, "IS DISTINCT FROM")
def or_sql(self, expression: sqlglot.expressions.Or) -> str:
1817    def or_sql(self, expression: exp.Or) -> str:
1818        return self.connector_sql(expression, "OR")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
1820    def slice_sql(self, expression: exp.Slice) -> str:
1821        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
1823    def sub_sql(self, expression: exp.Sub) -> str:
1824        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
1826    def trycast_sql(self, expression: exp.TryCast) -> str:
1827        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
1829    def use_sql(self, expression: exp.Use) -> str:
1830        kind = self.sql(expression, "kind")
1831        kind = f" {kind}" if kind else ""
1832        this = self.sql(expression, "this")
1833        this = f" {this}" if this else ""
1834        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
1836    def binary(self, expression: exp.Binary, op: str) -> str:
1837        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
1839    def function_fallback_sql(self, expression: exp.Func) -> str:
1840        args = []
1841        for arg_value in expression.args.values():
1842            if isinstance(arg_value, list):
1843                for value in arg_value:
1844                    args.append(value)
1845            else:
1846                args.append(arg_value)
1847
1848        return self.func(expression.sql_name(), *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
1850    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
1851        return f"{self.normalize_func(name)}({self.format_args(*args)})"
def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
1853    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
1854        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
1855        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
1856            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
1857        return ", ".join(arg_sqls)
def text_width(self, args: Iterable) -> int:
1859    def text_width(self, args: t.Iterable) -> int:
1860        return sum(len(arg) for arg in args)
def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]:
1862    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
1863        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)
def expressions( self, expression: sqlglot.expressions.Expression, key: Optional[str] = None, flat: bool = False, indent: bool = True, sep: str = ', ', prefix: str = '') -> str:
1865    def expressions(
1866        self,
1867        expression: exp.Expression,
1868        key: t.Optional[str] = None,
1869        flat: bool = False,
1870        indent: bool = True,
1871        sep: str = ", ",
1872        prefix: str = "",
1873    ) -> str:
1874        expressions = expression.args.get(key or "expressions")
1875
1876        if not expressions:
1877            return ""
1878
1879        if flat:
1880            return sep.join(self.sql(e) for e in expressions)
1881
1882        num_sqls = len(expressions)
1883
1884        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
1885        pad = " " * self.pad
1886        stripped_sep = sep.strip()
1887
1888        result_sqls = []
1889        for i, e in enumerate(expressions):
1890            sql = self.sql(e, comment=False)
1891            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
1892
1893            if self.pretty:
1894                if self._leading_comma:
1895                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
1896                else:
1897                    result_sqls.append(
1898                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
1899                    )
1900            else:
1901                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
1902
1903        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
1904        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:
1906    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
1907        flat = flat or isinstance(expression.parent, exp.Properties)
1908        expressions_sql = self.expressions(expression, flat=flat)
1909        if flat:
1910            return f"{op} {expressions_sql}"
1911        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
1913    def naked_property(self, expression: exp.Property) -> str:
1914        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
1915        if not property_name:
1916            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
1917        return f"{property_name} {self.sql(expression, 'this')}"
def set_operation(self, expression: sqlglot.expressions.Expression, op: str) -> str:
1919    def set_operation(self, expression: exp.Expression, op: str) -> str:
1920        this = self.sql(expression, "this")
1921        op = self.seg(op)
1922        return self.query_modifiers(
1923            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
1924        )
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
1926    def tag_sql(self, expression: exp.Tag) -> str:
1927        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
1929    def token_sql(self, token_type: TokenType) -> str:
1930        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
1932    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
1933        this = self.sql(expression, "this")
1934        expressions = self.no_identify(self.expressions, expression)
1935        expressions = (
1936            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
1937        )
1938        return f"{this}{expressions}"
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
1940    def joinhint_sql(self, expression: exp.JoinHint) -> str:
1941        this = self.sql(expression, "this")
1942        expressions = self.expressions(expression, flat=True)
1943        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
1945    def kwarg_sql(self, expression: exp.Kwarg) -> str:
1946        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
1948    def when_sql(self, expression: exp.When) -> str:
1949        this = self.sql(expression, "this")
1950        then_expression = expression.args.get("then")
1951        if isinstance(then_expression, exp.Insert):
1952            then = f"INSERT {self.sql(then_expression, 'this')}"
1953            if "expression" in then_expression.args:
1954                then += f" VALUES {self.sql(then_expression, 'expression')}"
1955        elif isinstance(then_expression, exp.Update):
1956            if isinstance(then_expression.args.get("expressions"), exp.Star):
1957                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
1958            else:
1959                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
1960        else:
1961            then = self.sql(then_expression)
1962        return f"WHEN {this} THEN {then}"
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
1964    def merge_sql(self, expression: exp.Merge) -> str:
1965        this = self.sql(expression, "this")
1966        using = f"USING {self.sql(expression, 'using')}"
1967        on = f"ON {self.sql(expression, 'on')}"
1968        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"