sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

       1from __future__ import annotations
       2
       3import logging
       4import typing as t
       5
       6from sqlglot import exp
       7from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
       8from sqlglot.helper import apply_index_offset, csv, seq_get, should_identify
       9from sqlglot.time import format_time
      10from sqlglot.tokens import TokenType
      11
      12logger = logging.getLogger("sqlglot")
      13
      14
      15class Generator:
      16    """
      17    Generator 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 | str): 'always': always quote, 'safe': quote identifiers if they don't contain an upcase, True defaults to always.
      29        normalize (bool): if set to True all identifiers will lower cased
      30        string_escape (str): specifies a string escape character. Default: '.
      31        identifier_escape (str): specifies an identifier escape character. Default: ".
      32        pad (int): determines padding in a formatted string. Default: 2.
      33        indent (int): determines the size of indentation in a formatted string. Default: 4.
      34        unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
      35        normalize_functions (str): normalize function names, "upper", "lower", or None
      36            Default: "upper"
      37        alias_post_tablesample (bool): if the table alias comes after tablesample
      38            Default: False
      39        unsupported_level (ErrorLevel): determines the generator's behavior when it encounters
      40            unsupported expressions. Default ErrorLevel.WARN.
      41        null_ordering (str): Indicates the default null ordering method to use if not explicitly set.
      42            Options are "nulls_are_small", "nulls_are_large", "nulls_are_last".
      43            Default: "nulls_are_small"
      44        max_unsupported (int): Maximum number of unsupported messages to include in a raised UnsupportedError.
      45            This is only relevant if unsupported_level is ErrorLevel.RAISE.
      46            Default: 3
      47        leading_comma (bool): if the the comma is leading or trailing in select statements
      48            Default: False
      49        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
      50            The default is on the smaller end because the length only represents a segment and not the true
      51            line length.
      52            Default: 80
      53        comments: Whether or not to preserve comments in the output SQL code.
      54            Default: True
      55    """
      56
      57    TRANSFORMS = {
      58        exp.DateAdd: lambda self, e: self.func(
      59            "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      60        ),
      61        exp.TsOrDsAdd: lambda self, e: self.func(
      62            "TS_OR_DS_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      63        ),
      64        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
      65        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
      66        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
      67        exp.ExternalProperty: lambda self, e: "EXTERNAL",
      68        exp.LanguageProperty: lambda self, e: self.naked_property(e),
      69        exp.LocationProperty: lambda self, e: self.naked_property(e),
      70        exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
      71        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
      72        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
      73        exp.OnCommitProperty: lambda self, e: "ON COMMIT PRESERVE ROWS",
      74        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
      75        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
      76        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
      77        exp.TemporaryProperty: lambda self, e: f"{'GLOBAL ' if e.args.get('global_') else ''}TEMPORARY",
      78        exp.TransientProperty: lambda self, e: "TRANSIENT",
      79        exp.VolatilityProperty: lambda self, e: e.name,
      80        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
      81        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
      82        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
      83        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
      84        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
      85        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
      86        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
      87        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
      88        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
      89        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
      90        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
      91        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
      92        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
      93    }
      94
      95    # Whether or not null ordering is supported in order by
      96    NULL_ORDERING_SUPPORTED = True
      97
      98    # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
      99    LOCKING_READS_SUPPORTED = False
     100
     101    # Always do union distinct or union all
     102    EXPLICIT_UNION = False
     103
     104    # Wrap derived values in parens, usually standard but spark doesn't support it
     105    WRAP_DERIVED_VALUES = True
     106
     107    # Whether or not create function uses an AS before the RETURN
     108    CREATE_FUNCTION_RETURN_AS = True
     109
     110    # Whether or not MERGE ... WHEN MATCHED BY SOURCE is allowed
     111    MATCHED_BY_SOURCE = True
     112
     113    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        returning = self.sql(expression, "returning")
     690        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
     691        return self.prepend_ctes(expression, sql)
     692
     693    def drop_sql(self, expression: exp.Drop) -> str:
     694        this = self.sql(expression, "this")
     695        kind = expression.args["kind"]
     696        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
     697        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
     698        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
     699        cascade = " CASCADE" if expression.args.get("cascade") else ""
     700        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}"
     701
     702    def except_sql(self, expression: exp.Except) -> str:
     703        return self.prepend_ctes(
     704            expression,
     705            self.set_operation(expression, self.except_op(expression)),
     706        )
     707
     708    def except_op(self, expression: exp.Except) -> str:
     709        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
     710
     711    def fetch_sql(self, expression: exp.Fetch) -> str:
     712        direction = expression.args.get("direction")
     713        direction = f" {direction.upper()}" if direction else ""
     714        count = expression.args.get("count")
     715        count = f" {count}" if count else ""
     716        return f"{self.seg('FETCH')}{direction}{count} ROWS ONLY"
     717
     718    def filter_sql(self, expression: exp.Filter) -> str:
     719        this = self.sql(expression, "this")
     720        where = self.sql(expression, "expression")[1:]  # where has a leading space
     721        return f"{this} FILTER({where})"
     722
     723    def hint_sql(self, expression: exp.Hint) -> str:
     724        if self.sql(expression, "this"):
     725            self.unsupported("Hints are not supported")
     726        return ""
     727
     728    def index_sql(self, expression: exp.Index) -> str:
     729        this = self.sql(expression, "this")
     730        table = self.sql(expression, "table")
     731        columns = self.sql(expression, "columns")
     732        return f"{this} ON {table} {columns}"
     733
     734    def identifier_sql(self, expression: exp.Identifier) -> str:
     735        text = expression.name
     736        text = text.lower() if self.normalize else text
     737        text = text.replace(self.identifier_end, self._escaped_identifier_end)
     738        if expression.args.get("quoted") or should_identify(text, self.identify):
     739            text = f"{self.identifier_start}{text}{self.identifier_end}"
     740        return text
     741
     742    def national_sql(self, expression: exp.National) -> str:
     743        return f"N{self.sql(expression, 'this')}"
     744
     745    def partition_sql(self, expression: exp.Partition) -> str:
     746        return f"PARTITION({self.expressions(expression)})"
     747
     748    def properties_sql(self, expression: exp.Properties) -> str:
     749        root_properties = []
     750        with_properties = []
     751
     752        for p in expression.expressions:
     753            p_loc = self.PROPERTIES_LOCATION[p.__class__]
     754            if p_loc == exp.Properties.Location.POST_WITH:
     755                with_properties.append(p)
     756            elif p_loc == exp.Properties.Location.POST_SCHEMA:
     757                root_properties.append(p)
     758
     759        return self.root_properties(
     760            exp.Properties(expressions=root_properties)
     761        ) + self.with_properties(exp.Properties(expressions=with_properties))
     762
     763    def root_properties(self, properties: exp.Properties) -> str:
     764        if properties.expressions:
     765            return self.sep() + self.expressions(properties, indent=False, sep=" ")
     766        return ""
     767
     768    def properties(
     769        self,
     770        properties: exp.Properties,
     771        prefix: str = "",
     772        sep: str = ", ",
     773        suffix: str = "",
     774        wrapped: bool = True,
     775    ) -> str:
     776        if properties.expressions:
     777            expressions = self.expressions(properties, sep=sep, indent=False)
     778            expressions = self.wrap(expressions) if wrapped else expressions
     779            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
     780        return ""
     781
     782    def with_properties(self, properties: exp.Properties) -> str:
     783        return self.properties(properties, prefix=self.seg("WITH"))
     784
     785    def locate_properties(
     786        self, properties: exp.Properties
     787    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
     788        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
     789            key: [] for key in exp.Properties.Location
     790        }
     791
     792        for p in properties.expressions:
     793            p_loc = self.PROPERTIES_LOCATION[p.__class__]
     794            if p_loc == exp.Properties.Location.POST_NAME:
     795                properties_locs[exp.Properties.Location.POST_NAME].append(p)
     796            elif p_loc == exp.Properties.Location.POST_INDEX:
     797                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
     798            elif p_loc == exp.Properties.Location.POST_SCHEMA:
     799                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
     800            elif p_loc == exp.Properties.Location.POST_WITH:
     801                properties_locs[exp.Properties.Location.POST_WITH].append(p)
     802            elif p_loc == exp.Properties.Location.POST_CREATE:
     803                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
     804            elif p_loc == exp.Properties.Location.POST_ALIAS:
     805                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
     806            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
     807                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
     808            elif p_loc == exp.Properties.Location.UNSUPPORTED:
     809                self.unsupported(f"Unsupported property {p.key}")
     810
     811        return properties_locs
     812
     813    def property_sql(self, expression: exp.Property) -> str:
     814        property_cls = expression.__class__
     815        if property_cls == exp.Property:
     816            return f"{expression.name}={self.sql(expression, 'value')}"
     817
     818        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
     819        if not property_name:
     820            self.unsupported(f"Unsupported property {expression.key}")
     821
     822        return f"{property_name}={self.sql(expression, 'this')}"
     823
     824    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
     825        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
     826        options = f" {options}" if options else ""
     827        return f"LIKE {self.sql(expression, 'this')}{options}"
     828
     829    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
     830        no = "NO " if expression.args.get("no") else ""
     831        protection = " PROTECTION" if expression.args.get("protection") else ""
     832        return f"{no}FALLBACK{protection}"
     833
     834    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
     835        no = "NO " if expression.args.get("no") else ""
     836        dual = "DUAL " if expression.args.get("dual") else ""
     837        before = "BEFORE " if expression.args.get("before") else ""
     838        return f"{no}{dual}{before}JOURNAL"
     839
     840    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
     841        freespace = self.sql(expression, "this")
     842        percent = " PERCENT" if expression.args.get("percent") else ""
     843        return f"FREESPACE={freespace}{percent}"
     844
     845    def afterjournalproperty_sql(self, expression: exp.AfterJournalProperty) -> str:
     846        no = "NO " if expression.args.get("no") else ""
     847        dual = "DUAL " if expression.args.get("dual") else ""
     848        local = ""
     849        if expression.args.get("local") is not None:
     850            local = "LOCAL " if expression.args.get("local") else "NOT LOCAL "
     851        return f"{no}{dual}{local}AFTER JOURNAL"
     852
     853    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
     854        if expression.args.get("default"):
     855            property = "DEFAULT"
     856        elif expression.args.get("on"):
     857            property = "ON"
     858        else:
     859            property = "OFF"
     860        return f"CHECKSUM={property}"
     861
     862    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
     863        if expression.args.get("no"):
     864            return "NO MERGEBLOCKRATIO"
     865        if expression.args.get("default"):
     866            return "DEFAULT MERGEBLOCKRATIO"
     867
     868        percent = " PERCENT" if expression.args.get("percent") else ""
     869        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
     870
     871    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
     872        default = expression.args.get("default")
     873        min = expression.args.get("min")
     874        if default is not None or min is not None:
     875            if default:
     876                property = "DEFAULT"
     877            elif min:
     878                property = "MINIMUM"
     879            else:
     880                property = "MAXIMUM"
     881            return f"{property} DATABLOCKSIZE"
     882        else:
     883            units = expression.args.get("units")
     884            units = f" {units}" if units else ""
     885            return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
     886
     887    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
     888        autotemp = expression.args.get("autotemp")
     889        always = expression.args.get("always")
     890        default = expression.args.get("default")
     891        manual = expression.args.get("manual")
     892        never = expression.args.get("never")
     893
     894        if autotemp is not None:
     895            property = f"AUTOTEMP({self.expressions(autotemp)})"
     896        elif always:
     897            property = "ALWAYS"
     898        elif default:
     899            property = "DEFAULT"
     900        elif manual:
     901            property = "MANUAL"
     902        elif never:
     903            property = "NEVER"
     904        return f"BLOCKCOMPRESSION={property}"
     905
     906    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
     907        no = expression.args.get("no")
     908        no = " NO" if no else ""
     909        concurrent = expression.args.get("concurrent")
     910        concurrent = " CONCURRENT" if concurrent else ""
     911
     912        for_ = ""
     913        if expression.args.get("for_all"):
     914            for_ = " FOR ALL"
     915        elif expression.args.get("for_insert"):
     916            for_ = " FOR INSERT"
     917        elif expression.args.get("for_none"):
     918            for_ = " FOR NONE"
     919        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
     920
     921    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
     922        kind = expression.args.get("kind")
     923        this: str = f" {this}" if expression.this else ""
     924        for_or_in = expression.args.get("for_or_in")
     925        lock_type = expression.args.get("lock_type")
     926        override = " OVERRIDE" if expression.args.get("override") else ""
     927        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
     928
     929    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
     930        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
     931        statistics = expression.args.get("statistics")
     932        statistics_sql = ""
     933        if statistics is not None:
     934            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
     935        return f"{data_sql}{statistics_sql}"
     936
     937    def insert_sql(self, expression: exp.Insert) -> str:
     938        overwrite = expression.args.get("overwrite")
     939
     940        if isinstance(expression.this, exp.Directory):
     941            this = "OVERWRITE " if overwrite else "INTO "
     942        else:
     943            this = "OVERWRITE TABLE " if overwrite else "INTO "
     944
     945        alternative = expression.args.get("alternative")
     946        alternative = f" OR {alternative} " if alternative else " "
     947        this = f"{this}{self.sql(expression, 'this')}"
     948
     949        exists = " IF EXISTS " if expression.args.get("exists") else " "
     950        partition_sql = (
     951            self.sql(expression, "partition") if expression.args.get("partition") else ""
     952        )
     953        expression_sql = self.sql(expression, "expression")
     954        returning = self.sql(expression, "returning")
     955        sep = self.sep() if partition_sql else ""
     956        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{returning}"
     957        return self.prepend_ctes(expression, sql)
     958
     959    def intersect_sql(self, expression: exp.Intersect) -> str:
     960        return self.prepend_ctes(
     961            expression,
     962            self.set_operation(expression, self.intersect_op(expression)),
     963        )
     964
     965    def intersect_op(self, expression: exp.Intersect) -> str:
     966        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
     967
     968    def introducer_sql(self, expression: exp.Introducer) -> str:
     969        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
     970
     971    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
     972        return expression.name.upper()
     973
     974    def returning_sql(self, expression: exp.Returning) -> str:
     975        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
     976
     977    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
     978        fields = expression.args.get("fields")
     979        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
     980        escaped = expression.args.get("escaped")
     981        escaped = f" ESCAPED BY {escaped}" if escaped else ""
     982        items = expression.args.get("collection_items")
     983        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
     984        keys = expression.args.get("map_keys")
     985        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
     986        lines = expression.args.get("lines")
     987        lines = f" LINES TERMINATED BY {lines}" if lines else ""
     988        null = expression.args.get("null")
     989        null = f" NULL DEFINED AS {null}" if null else ""
     990        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
     991
     992    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
     993        table = ".".join(
     994            part
     995            for part in [
     996                self.sql(expression, "catalog"),
     997                self.sql(expression, "db"),
     998                self.sql(expression, "this"),
     999            ]
    1000            if part
    1001        )
    1002
    1003        alias = self.sql(expression, "alias")
    1004        alias = f"{sep}{alias}" if alias else ""
    1005        hints = self.expressions(expression, key="hints", sep=", ", flat=True)
    1006        hints = f" WITH ({hints})" if hints else ""
    1007        laterals = self.expressions(expression, key="laterals", sep="")
    1008        joins = self.expressions(expression, key="joins", sep="")
    1009        pivots = self.expressions(expression, key="pivots", sep="")
    1010        system_time = expression.args.get("system_time")
    1011        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
    1012
    1013        return f"{table}{system_time}{alias}{hints}{laterals}{joins}{pivots}"
    1014
    1015    def tablesample_sql(self, expression: exp.TableSample, seed_prefix: str = "SEED") -> str:
    1016        if self.alias_post_tablesample and expression.this.alias:
    1017            this = self.sql(expression.this, "this")
    1018            alias = f" AS {self.sql(expression.this, 'alias')}"
    1019        else:
    1020            this = self.sql(expression, "this")
    1021            alias = ""
    1022        method = self.sql(expression, "method")
    1023        method = f"{method.upper()} " if method else ""
    1024        numerator = self.sql(expression, "bucket_numerator")
    1025        denominator = self.sql(expression, "bucket_denominator")
    1026        field = self.sql(expression, "bucket_field")
    1027        field = f" ON {field}" if field else ""
    1028        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1029        percent = self.sql(expression, "percent")
    1030        percent = f"{percent} PERCENT" if percent else ""
    1031        rows = self.sql(expression, "rows")
    1032        rows = f"{rows} ROWS" if rows else ""
    1033        size = self.sql(expression, "size")
    1034        seed = self.sql(expression, "seed")
    1035        seed = f" {seed_prefix} ({seed})" if seed else ""
    1036        kind = expression.args.get("kind", "TABLESAMPLE")
    1037        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"
    1038
    1039    def pivot_sql(self, expression: exp.Pivot) -> str:
    1040        this = self.sql(expression, "this")
    1041        alias = self.sql(expression, "alias")
    1042        alias = f" AS {alias}" if alias else ""
    1043        unpivot = expression.args.get("unpivot")
    1044        direction = "UNPIVOT" if unpivot else "PIVOT"
    1045        expressions = self.expressions(expression, key="expressions")
    1046        field = self.sql(expression, "field")
    1047        return f"{this} {direction}({expressions} FOR {field}){alias}"
    1048
    1049    def tuple_sql(self, expression: exp.Tuple) -> str:
    1050        return f"({self.expressions(expression, flat=True)})"
    1051
    1052    def update_sql(self, expression: exp.Update) -> str:
    1053        this = self.sql(expression, "this")
    1054        set_sql = self.expressions(expression, flat=True)
    1055        from_sql = self.sql(expression, "from")
    1056        where_sql = self.sql(expression, "where")
    1057        returning = self.sql(expression, "returning")
    1058        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
    1059        return self.prepend_ctes(expression, sql)
    1060
    1061    def values_sql(self, expression: exp.Values) -> str:
    1062        args = self.expressions(expression)
    1063        alias = self.sql(expression, "alias")
    1064        values = f"VALUES{self.seg('')}{args}"
    1065        values = (
    1066            f"({values})"
    1067            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1068            else values
    1069        )
    1070        return f"{values} AS {alias}" if alias else values
    1071
    1072    def var_sql(self, expression: exp.Var) -> str:
    1073        return self.sql(expression, "this")
    1074
    1075    def into_sql(self, expression: exp.Into) -> str:
    1076        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1077        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    1078        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    1079
    1080    def from_sql(self, expression: exp.From) -> str:
    1081        expressions = self.expressions(expression, flat=True)
    1082        return f"{self.seg('FROM')} {expressions}"
    1083
    1084    def group_sql(self, expression: exp.Group) -> str:
    1085        group_by = self.op_expressions("GROUP BY", expression)
    1086        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1087        grouping_sets = (
    1088            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1089        )
    1090
    1091        cube = expression.args.get("cube", [])
    1092        if seq_get(cube, 0) is True:
    1093            return f"{group_by}{self.seg('WITH CUBE')}"
    1094        else:
    1095            cube_sql = self.expressions(expression, key="cube", indent=False)
    1096            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1097
    1098        rollup = expression.args.get("rollup", [])
    1099        if seq_get(rollup, 0) is True:
    1100            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1101        else:
    1102            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1103            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1104
    1105        groupings = csv(grouping_sets, cube_sql, rollup_sql, sep=",")
    1106
    1107        if expression.args.get("expressions") and groupings:
    1108            group_by = f"{group_by},"
    1109
    1110        return f"{group_by}{groupings}"
    1111
    1112    def having_sql(self, expression: exp.Having) -> str:
    1113        this = self.indent(self.sql(expression, "this"))
    1114        return f"{self.seg('HAVING')}{self.sep()}{this}"
    1115
    1116    def join_sql(self, expression: exp.Join) -> str:
    1117        op_sql = self.seg(
    1118            " ".join(
    1119                op
    1120                for op in (
    1121                    "NATURAL" if expression.args.get("natural") else None,
    1122                    expression.side,
    1123                    expression.kind,
    1124                    "JOIN",
    1125                )
    1126                if op
    1127            )
    1128        )
    1129        on_sql = self.sql(expression, "on")
    1130        using = expression.args.get("using")
    1131
    1132        if not on_sql and using:
    1133            on_sql = csv(*(self.sql(column) for column in using))
    1134
    1135        if on_sql:
    1136            on_sql = self.indent(on_sql, skip_first=True)
    1137            space = self.seg(" " * self.pad) if self.pretty else " "
    1138            if using:
    1139                on_sql = f"{space}USING ({on_sql})"
    1140            else:
    1141                on_sql = f"{space}ON {on_sql}"
    1142
    1143        expression_sql = self.sql(expression, "expression")
    1144        this_sql = self.sql(expression, "this")
    1145        return f"{expression_sql}{op_sql} {this_sql}{on_sql}"
    1146
    1147    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    1148        args = self.expressions(expression, flat=True)
    1149        args = f"({args})" if len(args.split(",")) > 1 else args
    1150        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    1151
    1152    def lateral_sql(self, expression: exp.Lateral) -> str:
    1153        this = self.sql(expression, "this")
    1154
    1155        if isinstance(expression.this, exp.Subquery):
    1156            return f"LATERAL {this}"
    1157
    1158        if expression.args.get("view"):
    1159            alias = expression.args["alias"]
    1160            columns = self.expressions(alias, key="columns", flat=True)
    1161            table = f" {alias.name}" if alias.name else ""
    1162            columns = f" AS {columns}" if columns else ""
    1163            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1164            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1165
    1166        alias = self.sql(expression, "alias")
    1167        alias = f" AS {alias}" if alias else ""
    1168        return f"LATERAL {this}{alias}"
    1169
    1170    def limit_sql(self, expression: exp.Limit) -> str:
    1171        this = self.sql(expression, "this")
    1172        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"
    1173
    1174    def offset_sql(self, expression: exp.Offset) -> str:
    1175        this = self.sql(expression, "this")
    1176        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
    1177
    1178    def setitem_sql(self, expression: exp.SetItem) -> str:
    1179        kind = self.sql(expression, "kind")
    1180        kind = f"{kind} " if kind else ""
    1181        this = self.sql(expression, "this")
    1182        expressions = self.expressions(expression)
    1183        collate = self.sql(expression, "collate")
    1184        collate = f" COLLATE {collate}" if collate else ""
    1185        global_ = "GLOBAL " if expression.args.get("global") else ""
    1186        return f"{global_}{kind}{this}{expressions}{collate}"
    1187
    1188    def set_sql(self, expression: exp.Set) -> str:
    1189        expressions = (
    1190            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1191        )
    1192        return f"SET{expressions}"
    1193
    1194    def lock_sql(self, expression: exp.Lock) -> str:
    1195        if self.LOCKING_READS_SUPPORTED:
    1196            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
    1197            return self.seg(f"FOR {lock_type}")
    1198
    1199        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1200        return ""
    1201
    1202    def literal_sql(self, expression: exp.Literal) -> str:
    1203        text = expression.this or ""
    1204        if expression.is_string:
    1205            text = text.replace(self.quote_end, self._escaped_quote_end)
    1206            if self.pretty:
    1207                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1208            text = f"{self.quote_start}{text}{self.quote_end}"
    1209        return text
    1210
    1211    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1212        local = " LOCAL" if expression.args.get("local") else ""
    1213        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1214        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1215        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1216        partition = self.sql(expression, "partition")
    1217        partition = f" {partition}" if partition else ""
    1218        input_format = self.sql(expression, "input_format")
    1219        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1220        serde = self.sql(expression, "serde")
    1221        serde = f" SERDE {serde}" if serde else ""
    1222        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    1223
    1224    def null_sql(self, *_) -> str:
    1225        return "NULL"
    1226
    1227    def boolean_sql(self, expression: exp.Boolean) -> str:
    1228        return "TRUE" if expression.this else "FALSE"
    1229
    1230    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1231        this = self.sql(expression, "this")
    1232        this = f"{this} " if this else this
    1233        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
    1234
    1235    def cluster_sql(self, expression: exp.Cluster) -> str:
    1236        return self.op_expressions("CLUSTER BY", expression)
    1237
    1238    def distribute_sql(self, expression: exp.Distribute) -> str:
    1239        return self.op_expressions("DISTRIBUTE BY", expression)
    1240
    1241    def sort_sql(self, expression: exp.Sort) -> str:
    1242        return self.op_expressions("SORT BY", expression)
    1243
    1244    def ordered_sql(self, expression: exp.Ordered) -> str:
    1245        desc = expression.args.get("desc")
    1246        asc = not desc
    1247
    1248        nulls_first = expression.args.get("nulls_first")
    1249        nulls_last = not nulls_first
    1250        nulls_are_large = self.null_ordering == "nulls_are_large"
    1251        nulls_are_small = self.null_ordering == "nulls_are_small"
    1252        nulls_are_last = self.null_ordering == "nulls_are_last"
    1253
    1254        sort_order = " DESC" if desc else ""
    1255        nulls_sort_change = ""
    1256        if nulls_first and (
    1257            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1258        ):
    1259            nulls_sort_change = " NULLS FIRST"
    1260        elif (
    1261            nulls_last
    1262            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1263            and not nulls_are_last
    1264        ):
    1265            nulls_sort_change = " NULLS LAST"
    1266
    1267        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1268            self.unsupported(
    1269                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1270            )
    1271            nulls_sort_change = ""
    1272
    1273        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
    1274
    1275    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1276        partition = self.partition_by_sql(expression)
    1277        order = self.sql(expression, "order")
    1278        measures = self.sql(expression, "measures")
    1279        measures = self.seg(f"MEASURES {measures}") if measures else ""
    1280        rows = self.sql(expression, "rows")
    1281        rows = self.seg(rows) if rows else ""
    1282        after = self.sql(expression, "after")
    1283        after = self.seg(after) if after else ""
    1284        pattern = self.sql(expression, "pattern")
    1285        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1286        define = self.sql(expression, "define")
    1287        define = self.seg(f"DEFINE {define}") if define else ""
    1288        body = "".join(
    1289            (
    1290                partition,
    1291                order,
    1292                measures,
    1293                rows,
    1294                after,
    1295                pattern,
    1296                define,
    1297            )
    1298        )
    1299        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"
    1300
    1301    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1302        return csv(
    1303            *sqls,
    1304            *[self.sql(sql) for sql in expression.args.get("joins") or []],
    1305            self.sql(expression, "match"),
    1306            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
    1307            self.sql(expression, "where"),
    1308            self.sql(expression, "group"),
    1309            self.sql(expression, "having"),
    1310            self.sql(expression, "qualify"),
    1311            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
    1312            if expression.args.get("windows")
    1313            else "",
    1314            self.sql(expression, "distribute"),
    1315            self.sql(expression, "sort"),
    1316            self.sql(expression, "cluster"),
    1317            self.sql(expression, "order"),
    1318            self.sql(expression, "limit"),
    1319            self.sql(expression, "offset"),
    1320            self.sql(expression, "lock"),
    1321            self.sql(expression, "sample"),
    1322            sep="",
    1323        )
    1324
    1325    def select_sql(self, expression: exp.Select) -> str:
    1326        hint = self.sql(expression, "hint")
    1327        distinct = self.sql(expression, "distinct")
    1328        distinct = f" {distinct}" if distinct else ""
    1329        expressions = self.expressions(expression)
    1330        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1331        sql = self.query_modifiers(
    1332            expression,
    1333            f"SELECT{hint}{distinct}{expressions}",
    1334            self.sql(expression, "into", comment=False),
    1335            self.sql(expression, "from", comment=False),
    1336        )
    1337        return self.prepend_ctes(expression, sql)
    1338
    1339    def schema_sql(self, expression: exp.Schema) -> str:
    1340        this = self.sql(expression, "this")
    1341        this = f"{this} " if this else ""
    1342        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1343        return f"{this}{sql}"
    1344
    1345    def star_sql(self, expression: exp.Star) -> str:
    1346        except_ = self.expressions(expression, key="except", flat=True)
    1347        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1348        replace = self.expressions(expression, key="replace", flat=True)
    1349        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1350        return f"*{except_}{replace}"
    1351
    1352    def structkwarg_sql(self, expression: exp.StructKwarg) -> str:
    1353        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1354
    1355    def parameter_sql(self, expression: exp.Parameter) -> str:
    1356        this = self.sql(expression, "this")
    1357        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1358        return f"{self.PARAMETER_TOKEN}{this}"
    1359
    1360    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1361        this = self.sql(expression, "this")
    1362        kind = expression.text("kind")
    1363        if kind:
    1364            kind = f"{kind}."
    1365        return f"@@{kind}{this}"
    1366
    1367    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    1368        return f":{expression.name}" if expression.name else "?"
    1369
    1370    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1371        alias = self.sql(expression, "alias")
    1372        alias = f"{sep}{alias}" if alias else ""
    1373
    1374        sql = self.query_modifiers(
    1375            expression,
    1376            self.wrap(expression),
    1377            alias,
    1378            self.expressions(expression, key="pivots", sep=" "),
    1379        )
    1380
    1381        return self.prepend_ctes(expression, sql)
    1382
    1383    def qualify_sql(self, expression: exp.Qualify) -> str:
    1384        this = self.indent(self.sql(expression, "this"))
    1385        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    1386
    1387    def union_sql(self, expression: exp.Union) -> str:
    1388        return self.prepend_ctes(
    1389            expression,
    1390            self.set_operation(expression, self.union_op(expression)),
    1391        )
    1392
    1393    def union_op(self, expression: exp.Union) -> str:
    1394        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1395        kind = kind if expression.args.get("distinct") else " ALL"
    1396        return f"UNION{kind}"
    1397
    1398    def unnest_sql(self, expression: exp.Unnest) -> str:
    1399        args = self.expressions(expression, flat=True)
    1400        alias = expression.args.get("alias")
    1401        if alias and self.unnest_column_only:
    1402            columns = alias.columns
    1403            alias = self.sql(columns[0]) if columns else ""
    1404        else:
    1405            alias = self.sql(expression, "alias")
    1406        alias = f" AS {alias}" if alias else alias
    1407        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1408        offset = expression.args.get("offset")
    1409        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1410        return f"UNNEST({args}){ordinality}{alias}{offset}"
    1411
    1412    def where_sql(self, expression: exp.Where) -> str:
    1413        this = self.indent(self.sql(expression, "this"))
    1414        return f"{self.seg('WHERE')}{self.sep()}{this}"
    1415
    1416    def window_sql(self, expression: exp.Window) -> str:
    1417        this = self.sql(expression, "this")
    1418
    1419        partition = self.partition_by_sql(expression)
    1420
    1421        order = expression.args.get("order")
    1422        order_sql = self.order_sql(order, flat=True) if order else ""
    1423
    1424        partition_sql = partition + " " if partition and order else partition
    1425
    1426        spec = expression.args.get("spec")
    1427        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
    1428
    1429        alias = self.sql(expression, "alias")
    1430        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
    1431
    1432        if not partition and not order and not spec and alias:
    1433            return f"{this} {alias}"
    1434
    1435        window_args = alias + partition_sql + order_sql + spec_sql
    1436
    1437        return f"{this} ({window_args.strip()})"
    1438
    1439    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    1440        partition = self.expressions(expression, key="partition_by", flat=True)
    1441        return f"PARTITION BY {partition}" if partition else ""
    1442
    1443    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
    1444        kind = self.sql(expression, "kind")
    1445        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1446        end = (
    1447            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1448            or "CURRENT ROW"
    1449        )
    1450        return f"{kind} BETWEEN {start} AND {end}"
    1451
    1452    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    1453        this = self.sql(expression, "this")
    1454        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    1455        return f"{this} WITHIN GROUP ({expression_sql})"
    1456
    1457    def between_sql(self, expression: exp.Between) -> str:
    1458        this = self.sql(expression, "this")
    1459        low = self.sql(expression, "low")
    1460        high = self.sql(expression, "high")
    1461        return f"{this} BETWEEN {low} AND {high}"
    1462
    1463    def bracket_sql(self, expression: exp.Bracket) -> str:
    1464        expressions = apply_index_offset(expression.expressions, self.index_offset)
    1465        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1466
    1467        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    1468
    1469    def all_sql(self, expression: exp.All) -> str:
    1470        return f"ALL {self.wrap(expression)}"
    1471
    1472    def any_sql(self, expression: exp.Any) -> str:
    1473        this = self.sql(expression, "this")
    1474        if isinstance(expression.this, exp.Subqueryable):
    1475            this = self.wrap(this)
    1476        return f"ANY {this}"
    1477
    1478    def exists_sql(self, expression: exp.Exists) -> str:
    1479        return f"EXISTS{self.wrap(expression)}"
    1480
    1481    def case_sql(self, expression: exp.Case) -> str:
    1482        this = self.sql(expression, "this")
    1483        statements = [f"CASE {this}" if this else "CASE"]
    1484
    1485        for e in expression.args["ifs"]:
    1486            statements.append(f"WHEN {self.sql(e, 'this')}")
    1487            statements.append(f"THEN {self.sql(e, 'true')}")
    1488
    1489        default = self.sql(expression, "default")
    1490
    1491        if default:
    1492            statements.append(f"ELSE {default}")
    1493
    1494        statements.append("END")
    1495
    1496        if self.pretty and self.text_width(statements) > self._max_text_width:
    1497            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1498
    1499        return " ".join(statements)
    1500
    1501    def constraint_sql(self, expression: exp.Constraint) -> str:
    1502        this = self.sql(expression, "this")
    1503        expressions = self.expressions(expression, flat=True)
    1504        return f"CONSTRAINT {this} {expressions}"
    1505
    1506    def extract_sql(self, expression: exp.Extract) -> str:
    1507        this = self.sql(expression, "this")
    1508        expression_sql = self.sql(expression, "expression")
    1509        return f"EXTRACT({this} FROM {expression_sql})"
    1510
    1511    def trim_sql(self, expression: exp.Trim) -> str:
    1512        trim_type = self.sql(expression, "position")
    1513
    1514        if trim_type == "LEADING":
    1515            return self.func("LTRIM", expression.this)
    1516        elif trim_type == "TRAILING":
    1517            return self.func("RTRIM", expression.this)
    1518        else:
    1519            return self.func("TRIM", expression.this, expression.expression)
    1520
    1521    def concat_sql(self, expression: exp.Concat) -> str:
    1522        if len(expression.expressions) == 1:
    1523            return self.sql(expression.expressions[0])
    1524        return self.function_fallback_sql(expression)
    1525
    1526    def check_sql(self, expression: exp.Check) -> str:
    1527        this = self.sql(expression, key="this")
    1528        return f"CHECK ({this})"
    1529
    1530    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1531        expressions = self.expressions(expression, flat=True)
    1532        reference = self.sql(expression, "reference")
    1533        reference = f" {reference}" if reference else ""
    1534        delete = self.sql(expression, "delete")
    1535        delete = f" ON DELETE {delete}" if delete else ""
    1536        update = self.sql(expression, "update")
    1537        update = f" ON UPDATE {update}" if update else ""
    1538        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
    1539
    1540    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1541        expressions = self.expressions(expression, flat=True)
    1542        options = self.expressions(expression, "options", flat=True, sep=" ")
    1543        options = f" {options}" if options else ""
    1544        return f"PRIMARY KEY ({expressions}){options}"
    1545
    1546    def unique_sql(self, expression: exp.Unique) -> str:
    1547        columns = self.expressions(expression, key="expressions")
    1548        return f"UNIQUE ({columns})"
    1549
    1550    def if_sql(self, expression: exp.If) -> str:
    1551        return self.case_sql(
    1552            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1553        )
    1554
    1555    def in_sql(self, expression: exp.In) -> str:
    1556        query = expression.args.get("query")
    1557        unnest = expression.args.get("unnest")
    1558        field = expression.args.get("field")
    1559        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1560
    1561        if query:
    1562            in_sql = self.wrap(query)
    1563        elif unnest:
    1564            in_sql = self.in_unnest_op(unnest)
    1565        elif field:
    1566            in_sql = self.sql(field)
    1567        else:
    1568            in_sql = f"({self.expressions(expression, flat=True)})"
    1569
    1570        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    1571
    1572    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    1573        return f"(SELECT {self.sql(unnest)})"
    1574
    1575    def interval_sql(self, expression: exp.Interval) -> str:
    1576        this = expression.args.get("this")
    1577        if this:
    1578            this = (
    1579                f" {this}"
    1580                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
    1581                else f" ({this})"
    1582            )
    1583        else:
    1584            this = ""
    1585        unit = self.sql(expression, "unit")
    1586        unit = f" {unit}" if unit else ""
    1587        return f"INTERVAL{this}{unit}"
    1588
    1589    def return_sql(self, expression: exp.Return) -> str:
    1590        return f"RETURN {self.sql(expression, 'this')}"
    1591
    1592    def reference_sql(self, expression: exp.Reference) -> str:
    1593        this = self.sql(expression, "this")
    1594        expressions = self.expressions(expression, flat=True)
    1595        expressions = f"({expressions})" if expressions else ""
    1596        options = self.expressions(expression, "options", flat=True, sep=" ")
    1597        options = f" {options}" if options else ""
    1598        return f"REFERENCES {this}{expressions}{options}"
    1599
    1600    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    1601        return self.func(expression.name, *expression.expressions)
    1602
    1603    def paren_sql(self, expression: exp.Paren) -> str:
    1604        if isinstance(expression.unnest(), exp.Select):
    1605            sql = self.wrap(expression)
    1606        else:
    1607            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1608            sql = f"({sql}{self.seg(')', sep='')}"
    1609
    1610        return self.prepend_ctes(expression, sql)
    1611
    1612    def neg_sql(self, expression: exp.Neg) -> str:
    1613        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    1614        this_sql = self.sql(expression, "this")
    1615        sep = " " if this_sql[0] == "-" else ""
    1616        return f"-{sep}{this_sql}"
    1617
    1618    def not_sql(self, expression: exp.Not) -> str:
    1619        return f"NOT {self.sql(expression, 'this')}"
    1620
    1621    def alias_sql(self, expression: exp.Alias) -> str:
    1622        to_sql = self.sql(expression, "alias")
    1623        to_sql = f" AS {to_sql}" if to_sql else ""
    1624        return f"{self.sql(expression, 'this')}{to_sql}"
    1625
    1626    def aliases_sql(self, expression: exp.Aliases) -> str:
    1627        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    1628
    1629    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1630        this = self.sql(expression, "this")
    1631        zone = self.sql(expression, "zone")
    1632        return f"{this} AT TIME ZONE {zone}"
    1633
    1634    def add_sql(self, expression: exp.Add) -> str:
    1635        return self.binary(expression, "+")
    1636
    1637    def and_sql(self, expression: exp.And) -> str:
    1638        return self.connector_sql(expression, "AND")
    1639
    1640    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1641        if not self.pretty:
    1642            return self.binary(expression, op)
    1643
    1644        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
    1645        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1646        return f"{sep}{op} ".join(sqls)
    1647
    1648    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    1649        return self.binary(expression, "&")
    1650
    1651    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    1652        return self.binary(expression, "<<")
    1653
    1654    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    1655        return f"~{self.sql(expression, 'this')}"
    1656
    1657    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    1658        return self.binary(expression, "|")
    1659
    1660    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    1661        return self.binary(expression, ">>")
    1662
    1663    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    1664        return self.binary(expression, "^")
    1665
    1666    def cast_sql(self, expression: exp.Cast) -> str:
    1667        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
    1668
    1669    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    1670        zone = self.sql(expression, "this")
    1671        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    1672
    1673    def collate_sql(self, expression: exp.Collate) -> str:
    1674        return self.binary(expression, "COLLATE")
    1675
    1676    def command_sql(self, expression: exp.Command) -> str:
    1677        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
    1678
    1679    def comment_sql(self, expression: exp.Comment) -> str:
    1680        this = self.sql(expression, "this")
    1681        kind = expression.args["kind"]
    1682        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1683        expression_sql = self.sql(expression, "expression")
    1684        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
    1685
    1686    def transaction_sql(self, expression: exp.Transaction) -> str:
    1687        return "BEGIN"
    1688
    1689    def commit_sql(self, expression: exp.Commit) -> str:
    1690        chain = expression.args.get("chain")
    1691        if chain is not None:
    1692            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    1693
    1694        return f"COMMIT{chain or ''}"
    1695
    1696    def rollback_sql(self, expression: exp.Rollback) -> str:
    1697        savepoint = expression.args.get("savepoint")
    1698        savepoint = f" TO {savepoint}" if savepoint else ""
    1699        return f"ROLLBACK{savepoint}"
    1700
    1701    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    1702        this = self.sql(expression, "this")
    1703
    1704        dtype = self.sql(expression, "dtype")
    1705        if dtype:
    1706            collate = self.sql(expression, "collate")
    1707            collate = f" COLLATE {collate}" if collate else ""
    1708            using = self.sql(expression, "using")
    1709            using = f" USING {using}" if using else ""
    1710            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    1711
    1712        default = self.sql(expression, "default")
    1713        if default:
    1714            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    1715
    1716        if not expression.args.get("drop"):
    1717            self.unsupported("Unsupported ALTER COLUMN syntax")
    1718
    1719        return f"ALTER COLUMN {this} DROP DEFAULT"
    1720
    1721    def renametable_sql(self, expression: exp.RenameTable) -> str:
    1722        this = self.sql(expression, "this")
    1723        return f"RENAME TO {this}"
    1724
    1725    def altertable_sql(self, expression: exp.AlterTable) -> str:
    1726        actions = expression.args["actions"]
    1727
    1728        if isinstance(actions[0], exp.ColumnDef):
    1729            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
    1730        elif isinstance(actions[0], exp.Schema):
    1731            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
    1732        elif isinstance(actions[0], exp.Delete):
    1733            actions = self.expressions(expression, "actions", flat=True)
    1734        else:
    1735            actions = self.expressions(expression, "actions")
    1736
    1737        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1738        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
    1739
    1740    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    1741        expressions = self.expressions(expression)
    1742        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1743        return f"DROP{exists}{expressions}"
    1744
    1745    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    1746        this = self.sql(expression, "this")
    1747        expression_ = self.sql(expression, "expression")
    1748        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    1749
    1750        enforced = expression.args.get("enforced")
    1751        if enforced is not None:
    1752            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    1753
    1754        return f"{add_constraint} {expression_}"
    1755
    1756    def distinct_sql(self, expression: exp.Distinct) -> str:
    1757        this = self.expressions(expression, flat=True)
    1758        this = f" {this}" if this else ""
    1759
    1760        on = self.sql(expression, "on")
    1761        on = f" ON {on}" if on else ""
    1762        return f"DISTINCT{this}{on}"
    1763
    1764    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    1765        return f"{self.sql(expression, 'this')} IGNORE NULLS"
    1766
    1767    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    1768        return f"{self.sql(expression, 'this')} RESPECT NULLS"
    1769
    1770    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    1771        return self.sql(
    1772            exp.Cast(
    1773                this=exp.Div(this=expression.this, expression=expression.expression),
    1774                to=exp.DataType(this=exp.DataType.Type.INT),
    1775            )
    1776        )
    1777
    1778    def dpipe_sql(self, expression: exp.DPipe) -> str:
    1779        return self.binary(expression, "||")
    1780
    1781    def div_sql(self, expression: exp.Div) -> str:
    1782        return self.binary(expression, "/")
    1783
    1784    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    1785        return self.binary(expression, "OVERLAPS")
    1786
    1787    def distance_sql(self, expression: exp.Distance) -> str:
    1788        return self.binary(expression, "<->")
    1789
    1790    def dot_sql(self, expression: exp.Dot) -> str:
    1791        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    1792
    1793    def eq_sql(self, expression: exp.EQ) -> str:
    1794        return self.binary(expression, "=")
    1795
    1796    def escape_sql(self, expression: exp.Escape) -> str:
    1797        return self.binary(expression, "ESCAPE")
    1798
    1799    def glob_sql(self, expression: exp.Glob) -> str:
    1800        return self.binary(expression, "GLOB")
    1801
    1802    def gt_sql(self, expression: exp.GT) -> str:
    1803        return self.binary(expression, ">")
    1804
    1805    def gte_sql(self, expression: exp.GTE) -> str:
    1806        return self.binary(expression, ">=")
    1807
    1808    def ilike_sql(self, expression: exp.ILike) -> str:
    1809        return self.binary(expression, "ILIKE")
    1810
    1811    def is_sql(self, expression: exp.Is) -> str:
    1812        return self.binary(expression, "IS")
    1813
    1814    def like_sql(self, expression: exp.Like) -> str:
    1815        return self.binary(expression, "LIKE")
    1816
    1817    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    1818        return self.binary(expression, "SIMILAR TO")
    1819
    1820    def lt_sql(self, expression: exp.LT) -> str:
    1821        return self.binary(expression, "<")
    1822
    1823    def lte_sql(self, expression: exp.LTE) -> str:
    1824        return self.binary(expression, "<=")
    1825
    1826    def mod_sql(self, expression: exp.Mod) -> str:
    1827        return self.binary(expression, "%")
    1828
    1829    def mul_sql(self, expression: exp.Mul) -> str:
    1830        return self.binary(expression, "*")
    1831
    1832    def neq_sql(self, expression: exp.NEQ) -> str:
    1833        return self.binary(expression, "<>")
    1834
    1835    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    1836        return self.binary(expression, "IS NOT DISTINCT FROM")
    1837
    1838    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    1839        return self.binary(expression, "IS DISTINCT FROM")
    1840
    1841    def or_sql(self, expression: exp.Or) -> str:
    1842        return self.connector_sql(expression, "OR")
    1843
    1844    def slice_sql(self, expression: exp.Slice) -> str:
    1845        return self.binary(expression, ":")
    1846
    1847    def sub_sql(self, expression: exp.Sub) -> str:
    1848        return self.binary(expression, "-")
    1849
    1850    def trycast_sql(self, expression: exp.TryCast) -> str:
    1851        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
    1852
    1853    def use_sql(self, expression: exp.Use) -> str:
    1854        kind = self.sql(expression, "kind")
    1855        kind = f" {kind}" if kind else ""
    1856        this = self.sql(expression, "this")
    1857        this = f" {this}" if this else ""
    1858        return f"USE{kind}{this}"
    1859
    1860    def binary(self, expression: exp.Binary, op: str) -> str:
    1861        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
    1862
    1863    def function_fallback_sql(self, expression: exp.Func) -> str:
    1864        args = []
    1865        for arg_value in expression.args.values():
    1866            if isinstance(arg_value, list):
    1867                for value in arg_value:
    1868                    args.append(value)
    1869            else:
    1870                args.append(arg_value)
    1871
    1872        return self.func(expression.sql_name(), *args)
    1873
    1874    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
    1875        return f"{self.normalize_func(name)}({self.format_args(*args)})"
    1876
    1877    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    1878        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    1879        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
    1880            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    1881        return ", ".join(arg_sqls)
    1882
    1883    def text_width(self, args: t.Iterable) -> int:
    1884        return sum(len(arg) for arg in args)
    1885
    1886    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    1887        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)
    1888
    1889    def expressions(
    1890        self,
    1891        expression: exp.Expression,
    1892        key: t.Optional[str] = None,
    1893        flat: bool = False,
    1894        indent: bool = True,
    1895        sep: str = ", ",
    1896        prefix: str = "",
    1897    ) -> str:
    1898        expressions = expression.args.get(key or "expressions")
    1899
    1900        if not expressions:
    1901            return ""
    1902
    1903        if flat:
    1904            return sep.join(self.sql(e) for e in expressions)
    1905
    1906        num_sqls = len(expressions)
    1907
    1908        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    1909        pad = " " * self.pad
    1910        stripped_sep = sep.strip()
    1911
    1912        result_sqls = []
    1913        for i, e in enumerate(expressions):
    1914            sql = self.sql(e, comment=False)
    1915            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    1916
    1917            if self.pretty:
    1918                if self._leading_comma:
    1919                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    1920                else:
    1921                    result_sqls.append(
    1922                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    1923                    )
    1924            else:
    1925                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    1926
    1927        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    1928        return self.indent(result_sql, skip_first=False) if indent else result_sql
    1929
    1930    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    1931        flat = flat or isinstance(expression.parent, exp.Properties)
    1932        expressions_sql = self.expressions(expression, flat=flat)
    1933        if flat:
    1934            return f"{op} {expressions_sql}"
    1935        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    1936
    1937    def naked_property(self, expression: exp.Property) -> str:
    1938        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    1939        if not property_name:
    1940            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    1941        return f"{property_name} {self.sql(expression, 'this')}"
    1942
    1943    def set_operation(self, expression: exp.Expression, op: str) -> str:
    1944        this = self.sql(expression, "this")
    1945        op = self.seg(op)
    1946        return self.query_modifiers(
    1947            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    1948        )
    1949
    1950    def tag_sql(self, expression: exp.Tag) -> str:
    1951        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    1952
    1953    def token_sql(self, token_type: TokenType) -> str:
    1954        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    1955
    1956    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    1957        this = self.sql(expression, "this")
    1958        expressions = self.no_identify(self.expressions, expression)
    1959        expressions = (
    1960            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    1961        )
    1962        return f"{this}{expressions}"
    1963
    1964    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    1965        this = self.sql(expression, "this")
    1966        expressions = self.expressions(expression, flat=True)
    1967        return f"{this}({expressions})"
    1968
    1969    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    1970        return self.binary(expression, "=>")
    1971
    1972    def when_sql(self, expression: exp.When) -> str:
    1973        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    1974        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    1975        condition = self.sql(expression, "condition")
    1976        condition = f" AND {condition}" if condition else ""
    1977
    1978        then_expression = expression.args.get("then")
    1979        if isinstance(then_expression, exp.Insert):
    1980            then = f"INSERT {self.sql(then_expression, 'this')}"
    1981            if "expression" in then_expression.args:
    1982                then += f" VALUES {self.sql(then_expression, 'expression')}"
    1983        elif isinstance(then_expression, exp.Update):
    1984            if isinstance(then_expression.args.get("expressions"), exp.Star):
    1985                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    1986            else:
    1987                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    1988        else:
    1989            then = self.sql(then_expression)
    1990        return f"WHEN {matched}{source}{condition} THEN {then}"
    1991
    1992    def merge_sql(self, expression: exp.Merge) -> str:
    1993        this = self.sql(expression, "this")
    1994        using = f"USING {self.sql(expression, 'using')}"
    1995        on = f"ON {self.sql(expression, 'on')}"
    1996        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"
    1997
    1998    def tochar_sql(self, expression: exp.ToChar) -> str:
    1999        if expression.args.get("format"):
    2000            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2001
    2002        return self.sql(exp.cast(expression.this, "text"))

class Generator: View Source

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

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 | str): 'always': always quote, 'safe': quote identifiers if they don't contain an upcase, True defaults to always.
- 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) View Source

    215    def __init__(
    216        self,
    217        time_mapping=None,
    218        time_trie=None,
    219        pretty=None,
    220        quote_start=None,
    221        quote_end=None,
    222        identifier_start=None,
    223        identifier_end=None,
    224        identify=False,
    225        normalize=False,
    226        string_escape=None,
    227        identifier_escape=None,
    228        pad=2,
    229        indent=2,
    230        index_offset=0,
    231        unnest_column_only=False,
    232        alias_post_tablesample=False,
    233        normalize_functions="upper",
    234        unsupported_level=ErrorLevel.WARN,
    235        null_ordering=None,
    236        max_unsupported=3,
    237        leading_comma=False,
    238        max_text_width=80,
    239        comments=True,
    240    ):
    241        import sqlglot
    242
    243        self.time_mapping = time_mapping or {}
    244        self.time_trie = time_trie
    245        self.pretty = pretty if pretty is not None else sqlglot.pretty
    246        self.quote_start = quote_start or "'"
    247        self.quote_end = quote_end or "'"
    248        self.identifier_start = identifier_start or '"'
    249        self.identifier_end = identifier_end or '"'
    250        self.identify = identify
    251        self.normalize = normalize
    252        self.string_escape = string_escape or "'"
    253        self.identifier_escape = identifier_escape or '"'
    254        self.pad = pad
    255        self.index_offset = index_offset
    256        self.unnest_column_only = unnest_column_only
    257        self.alias_post_tablesample = alias_post_tablesample
    258        self.normalize_functions = normalize_functions
    259        self.unsupported_level = unsupported_level
    260        self.unsupported_messages = []
    261        self.max_unsupported = max_unsupported
    262        self.null_ordering = null_ordering
    263        self._indent = indent
    264        self._escaped_quote_end = self.string_escape + self.quote_end
    265        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
    266        self._leading_comma = leading_comma
    267        self._max_text_width = max_text_width
    268        self._comments = comments

def generate(self, expression: Optional[sqlglot.expressions.Expression]) -> str: View Source

    270    def generate(self, expression: t.Optional[exp.Expression]) -> str:
    271        """
    272        Generates a SQL string by interpreting the given syntax tree.
    273
    274        Args
    275            expression: the syntax tree.
    276
    277        Returns
    278            the SQL string.
    279        """
    280        self.unsupported_messages = []
    281        sql = self.sql(expression).strip()
    282
    283        if self.unsupported_level == ErrorLevel.IGNORE:
    284            return sql
    285
    286        if self.unsupported_level == ErrorLevel.WARN:
    287            for msg in self.unsupported_messages:
    288                logger.warning(msg)
    289        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    290            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    291
    292        if self.pretty:
    293            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    294        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: View Source

    296    def unsupported(self, message: str) -> None:
    297        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    298            raise UnsupportedError(message)
    299        self.unsupported_messages.append(message)

def sep(self, sep: str = ' ') -> str: View Source

    301    def sep(self, sep: str = " ") -> str:
    302        return f"{sep.strip()}\n" if self.pretty else sep

def seg(self, sql: str, sep: str = ' ') -> str: View Source

    304    def seg(self, sql: str, sep: str = " ") -> str:
    305        return f"{self.sep(sep)}{sql}"

def pad_comment(self, comment: str) -> str: View Source

    307    def pad_comment(self, comment: str) -> str:
    308        comment = " " + comment if comment[0].strip() else comment
    309        comment = comment + " " if comment[-1].strip() else comment
    310        return comment

def maybe_comment(self, sql: str, expression: sqlglot.expressions.Expression) -> str: View Source

    312    def maybe_comment(self, sql: str, expression: exp.Expression) -> str:
    313        comments = expression.comments if self._comments else None
    314
    315        if not comments:
    316            return sql
    317
    318        sep = "\n" if self.pretty else " "
    319        comments_sql = sep.join(
    320            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    321        )
    322
    323        if not comments_sql:
    324            return sql
    325
    326        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    327            return f"{comments_sql}{self.sep()}{sql}"
    328
    329        return f"{sql} {comments_sql}"

def wrap(self, expression: sqlglot.expressions.Expression | str) -> str: View Source

    331    def wrap(self, expression: exp.Expression | str) -> str:
    332        this_sql = self.indent(
    333            self.sql(expression)
    334            if isinstance(expression, (exp.Select, exp.Union))
    335            else self.sql(expression, "this"),
    336            level=1,
    337            pad=0,
    338        )
    339        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str: View Source

    341    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    342        original = self.identify
    343        self.identify = False
    344        result = func(*args, **kwargs)
    345        self.identify = original
    346        return result

def normalize_func(self, name: str) -> str: View Source

    348    def normalize_func(self, name: str) -> str:
    349        if self.normalize_functions == "upper":
    350            return name.upper()
    351        if self.normalize_functions == "lower":
    352            return name.lower()
    353        return name

def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str: View Source

    355    def indent(
    356        self,
    357        sql: str,
    358        level: int = 0,
    359        pad: t.Optional[int] = None,
    360        skip_first: bool = False,
    361        skip_last: bool = False,
    362    ) -> str:
    363        if not self.pretty:
    364            return sql
    365
    366        pad = self.pad if pad is None else pad
    367        lines = sql.split("\n")
    368
    369        return "\n".join(
    370            line
    371            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    372            else f"{' ' * (level * self._indent + pad)}{line}"
    373            for i, line in enumerate(lines)
    374        )

def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str: View Source

    376    def sql(
    377        self,
    378        expression: t.Optional[str | exp.Expression],
    379        key: t.Optional[str] = None,
    380        comment: bool = True,
    381    ) -> str:
    382        if not expression:
    383            return ""
    384
    385        if isinstance(expression, str):
    386            return expression
    387
    388        if key:
    389            return self.sql(expression.args.get(key))
    390
    391        transform = self.TRANSFORMS.get(expression.__class__)
    392
    393        if callable(transform):
    394            sql = transform(self, expression)
    395        elif transform:
    396            sql = transform
    397        elif isinstance(expression, exp.Expression):
    398            exp_handler_name = f"{expression.key}_sql"
    399
    400            if hasattr(self, exp_handler_name):
    401                sql = getattr(self, exp_handler_name)(expression)
    402            elif isinstance(expression, exp.Func):
    403                sql = self.function_fallback_sql(expression)
    404            elif isinstance(expression, exp.Property):
    405                sql = self.property_sql(expression)
    406            else:
    407                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    408        else:
    409            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    410
    411        return self.maybe_comment(sql, expression) if self._comments and comment else sql

def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str: View Source

    413    def uncache_sql(self, expression: exp.Uncache) -> str:
    414        table = self.sql(expression, "this")
    415        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    416        return f"UNCACHE TABLE{exists_sql} {table}"

def cache_sql(self, expression: sqlglot.expressions.Cache) -> str: View Source

    418    def cache_sql(self, expression: exp.Cache) -> str:
    419        lazy = " LAZY" if expression.args.get("lazy") else ""
    420        table = self.sql(expression, "this")
    421        options = expression.args.get("options")
    422        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    423        sql = self.sql(expression, "expression")
    424        sql = f" AS{self.sep()}{sql}" if sql else ""
    425        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    426        return self.prepend_ctes(expression, sql)

def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str: View Source

    428    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    429        if isinstance(expression.parent, exp.Cast):
    430            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    431        default = "DEFAULT " if expression.args.get("default") else ""
    432        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

def column_sql(self, expression: sqlglot.expressions.Column) -> str: View Source

    434    def column_sql(self, expression: exp.Column) -> str:
    435        return ".".join(
    436            self.sql(part)
    437            for part in (
    438                expression.args.get("catalog"),
    439                expression.args.get("db"),
    440                expression.args.get("table"),
    441                expression.args.get("this"),
    442            )
    443            if part
    444        )

def columndef_sql(self, expression: sqlglot.expressions.ColumnDef) -> str: View Source

    446    def columndef_sql(self, expression: exp.ColumnDef) -> str:
    447        column = self.sql(expression, "this")
    448        kind = self.sql(expression, "kind")
    449        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    450        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    451        kind = f" {kind}" if kind else ""
    452        constraints = f" {constraints}" if constraints else ""
    453
    454        return f"{exists}{column}{kind}{constraints}"

def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str: View Source

    456    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    457        this = self.sql(expression, "this")
    458        kind_sql = self.sql(expression, "kind")
    459        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

def autoincrementcolumnconstraint_sql(self, _) -> str: View Source

    461    def autoincrementcolumnconstraint_sql(self, _) -> str:
    462        return self.token_sql(TokenType.AUTO_INCREMENT)

def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str: View Source

    464    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    465        if isinstance(expression.this, list):
    466            this = self.wrap(self.expressions(expression, key="this", flat=True))
    467        else:
    468            this = self.sql(expression, "this")
    469
    470        return f"COMPRESS {this}"

def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str: View Source

    472    def generatedasidentitycolumnconstraint_sql(
    473        self, expression: exp.GeneratedAsIdentityColumnConstraint
    474    ) -> str:
    475        this = ""
    476        if expression.this is not None:
    477            this = " ALWAYS " if expression.this else " BY DEFAULT "
    478        start = expression.args.get("start")
    479        start = f"START WITH {start}" if start else ""
    480        increment = expression.args.get("increment")
    481        increment = f" INCREMENT BY {increment}" if increment else ""
    482        minvalue = expression.args.get("minvalue")
    483        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    484        maxvalue = expression.args.get("maxvalue")
    485        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    486        cycle = expression.args.get("cycle")
    487        cycle_sql = ""
    488        if cycle is not None:
    489            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    490            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    491        sequence_opts = ""
    492        if start or increment or cycle_sql:
    493            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    494            sequence_opts = f" ({sequence_opts.strip()})"
    495        return f"GENERATED{this}AS IDENTITY{sequence_opts}"

def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str: View Source

    497    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
    498        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"

def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str: View Source

    500    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    501        desc = expression.args.get("desc")
    502        if desc is not None:
    503            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    504        return f"PRIMARY KEY"

def uniquecolumnconstraint_sql(self, _) -> str: View Source

    506    def uniquecolumnconstraint_sql(self, _) -> str:
    507        return "UNIQUE"

def create_sql(self, expression: sqlglot.expressions.Create) -> str: View Source

    509    def create_sql(self, expression: exp.Create) -> str:
    510        kind = self.sql(expression, "kind").upper()
    511        properties = expression.args.get("properties")
    512        properties_exp = expression.copy()
    513        properties_locs = self.locate_properties(properties) if properties else {}
    514        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    515            exp.Properties.Location.POST_WITH
    516        ):
    517            properties_exp.set(
    518                "properties",
    519                exp.Properties(
    520                    expressions=[
    521                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    522                        *properties_locs[exp.Properties.Location.POST_WITH],
    523                    ]
    524                ),
    525            )
    526        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
    527            this_name = self.sql(expression.this, "this")
    528            this_properties = self.properties(
    529                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
    530                wrapped=False,
    531            )
    532            this_schema = f"({self.expressions(expression.this)})"
    533            this = f"{this_name}, {this_properties} {this_schema}"
    534            properties_sql = ""
    535        else:
    536            this = self.sql(expression, "this")
    537            properties_sql = self.sql(properties_exp, "properties")
    538        begin = " BEGIN" if expression.args.get("begin") else ""
    539        expression_sql = self.sql(expression, "expression")
    540        if expression_sql:
    541            expression_sql = f"{begin}{self.sep()}{expression_sql}"
    542
    543            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    544                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    545                    postalias_props_sql = self.properties(
    546                        exp.Properties(
    547                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    548                        ),
    549                        wrapped=False,
    550                    )
    551                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
    552                else:
    553                    expression_sql = f" AS{expression_sql}"
    554
    555        postindex_props_sql = ""
    556        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    557            postindex_props_sql = self.properties(
    558                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    559                wrapped=False,
    560                prefix=" ",
    561            )
    562
    563        indexes = expression.args.get("indexes")
    564        if indexes:
    565            indexes_sql: t.List[str] = []
    566            for index in indexes:
    567                ind_unique = " UNIQUE" if index.args.get("unique") else ""
    568                ind_primary = " PRIMARY" if index.args.get("primary") else ""
    569                ind_amp = " AMP" if index.args.get("amp") else ""
    570                ind_name = f" {index.name}" if index.name else ""
    571                ind_columns = (
    572                    f' ({self.expressions(index, key="columns", flat=True)})'
    573                    if index.args.get("columns")
    574                    else ""
    575                )
    576                ind_sql = f"{ind_unique}{ind_primary}{ind_amp} INDEX{ind_name}{ind_columns}"
    577
    578                if indexes_sql:
    579                    indexes_sql.append(ind_sql)
    580                else:
    581                    indexes_sql.append(
    582                        f"{ind_sql}{postindex_props_sql}"
    583                        if index.args.get("primary")
    584                        else f"{postindex_props_sql}{ind_sql}"
    585                    )
    586
    587            index_sql = "".join(indexes_sql)
    588        else:
    589            index_sql = postindex_props_sql
    590
    591        replace = " OR REPLACE" if expression.args.get("replace") else ""
    592        unique = " UNIQUE" if expression.args.get("unique") else ""
    593        volatile = " VOLATILE" if expression.args.get("volatile") else ""
    594
    595        postcreate_props_sql = ""
    596        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    597            postcreate_props_sql = self.properties(
    598                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    599                sep=" ",
    600                prefix=" ",
    601                wrapped=False,
    602            )
    603
    604        modifiers = "".join((replace, unique, volatile, postcreate_props_sql))
    605
    606        postexpression_props_sql = ""
    607        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    608            postexpression_props_sql = self.properties(
    609                exp.Properties(
    610                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    611                ),
    612                sep=" ",
    613                prefix=" ",
    614                wrapped=False,
    615            )
    616
    617        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    618        no_schema_binding = (
    619            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    620        )
    621
    622        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}"
    623        return self.prepend_ctes(expression, expression_sql)

def describe_sql(self, expression: sqlglot.expressions.Describe) -> str: View Source

    625    def describe_sql(self, expression: exp.Describe) -> str:
    626        return f"DESCRIBE {self.sql(expression, 'this')}"

def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str: View Source

    628    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    629        with_ = self.sql(expression, "with")
    630        if with_:
    631            sql = f"{with_}{self.sep()}{sql}"
    632        return sql

def with_sql(self, expression: sqlglot.expressions.With) -> str: View Source

    634    def with_sql(self, expression: exp.With) -> str:
    635        sql = self.expressions(expression, flat=True)
    636        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
    637
    638        return f"WITH {recursive}{sql}"

def cte_sql(self, expression: sqlglot.expressions.CTE) -> str: View Source

    640    def cte_sql(self, expression: exp.CTE) -> str:
    641        alias = self.sql(expression, "alias")
    642        return f"{alias} AS {self.wrap(expression)}"

def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str: View Source

    644    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    645        alias = self.sql(expression, "this")
    646        columns = self.expressions(expression, key="columns", flat=True)
    647        columns = f"({columns})" if columns else ""
    648        return f"{alias}{columns}"

def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str: View Source

    650    def bitstring_sql(self, expression: exp.BitString) -> str:
    651        return self.sql(expression, "this")

def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str: View Source

    653    def hexstring_sql(self, expression: exp.HexString) -> str:
    654        return self.sql(expression, "this")

def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str: View Source

    656    def datatype_sql(self, expression: exp.DataType) -> str:
    657        type_value = expression.this
    658        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
    659        nested = ""
    660        interior = self.expressions(expression, flat=True)
    661        values = ""
    662        if interior:
    663            if expression.args.get("nested"):
    664                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    665                if expression.args.get("values") is not None:
    666                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    667                    values = (
    668                        f"{delimiters[0]}{self.expressions(expression, 'values')}{delimiters[1]}"
    669                    )
    670            else:
    671                nested = f"({interior})"
    672
    673        return f"{type_sql}{nested}{values}"

def directory_sql(self, expression: sqlglot.expressions.Directory) -> str: View Source

    675    def directory_sql(self, expression: exp.Directory) -> str:
    676        local = "LOCAL " if expression.args.get("local") else ""
    677        row_format = self.sql(expression, "row_format")
    678        row_format = f" {row_format}" if row_format else ""
    679        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

def delete_sql(self, expression: sqlglot.expressions.Delete) -> str: View Source

    681    def delete_sql(self, expression: exp.Delete) -> str:
    682        this = self.sql(expression, "this")
    683        this = f" FROM {this}" if this else ""
    684        using_sql = (
    685            f" USING {self.expressions(expression, 'using', sep=', USING ')}"
    686            if expression.args.get("using")
    687            else ""
    688        )
    689        where_sql = self.sql(expression, "where")
    690        returning = self.sql(expression, "returning")
    691        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
    692        return self.prepend_ctes(expression, sql)

def drop_sql(self, expression: sqlglot.expressions.Drop) -> str: View Source

    694    def drop_sql(self, expression: exp.Drop) -> str:
    695        this = self.sql(expression, "this")
    696        kind = expression.args["kind"]
    697        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    698        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    699        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    700        cascade = " CASCADE" if expression.args.get("cascade") else ""
    701        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}"

def except_sql(self, expression: sqlglot.expressions.Except) -> str: View Source

    703    def except_sql(self, expression: exp.Except) -> str:
    704        return self.prepend_ctes(
    705            expression,
    706            self.set_operation(expression, self.except_op(expression)),
    707        )

def except_op(self, expression: sqlglot.expressions.Except) -> str: View Source

    709    def except_op(self, expression: exp.Except) -> str:
    710        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"

def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str: View Source

    712    def fetch_sql(self, expression: exp.Fetch) -> str:
    713        direction = expression.args.get("direction")
    714        direction = f" {direction.upper()}" if direction else ""
    715        count = expression.args.get("count")
    716        count = f" {count}" if count else ""
    717        return f"{self.seg('FETCH')}{direction}{count} ROWS ONLY"

def filter_sql(self, expression: sqlglot.expressions.Filter) -> str: View Source

    719    def filter_sql(self, expression: exp.Filter) -> str:
    720        this = self.sql(expression, "this")
    721        where = self.sql(expression, "expression")[1:]  # where has a leading space
    722        return f"{this} FILTER({where})"

def hint_sql(self, expression: sqlglot.expressions.Hint) -> str: View Source

    724    def hint_sql(self, expression: exp.Hint) -> str:
    725        if self.sql(expression, "this"):
    726            self.unsupported("Hints are not supported")
    727        return ""

def index_sql(self, expression: sqlglot.expressions.Index) -> str: View Source

    729    def index_sql(self, expression: exp.Index) -> str:
    730        this = self.sql(expression, "this")
    731        table = self.sql(expression, "table")
    732        columns = self.sql(expression, "columns")
    733        return f"{this} ON {table} {columns}"

def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str: View Source

    735    def identifier_sql(self, expression: exp.Identifier) -> str:
    736        text = expression.name
    737        text = text.lower() if self.normalize else text
    738        text = text.replace(self.identifier_end, self._escaped_identifier_end)
    739        if expression.args.get("quoted") or should_identify(text, self.identify):
    740            text = f"{self.identifier_start}{text}{self.identifier_end}"
    741        return text

def national_sql(self, expression: sqlglot.expressions.National) -> str: View Source

    743    def national_sql(self, expression: exp.National) -> str:
    744        return f"N{self.sql(expression, 'this')}"

def partition_sql(self, expression: sqlglot.expressions.Partition) -> str: View Source

    746    def partition_sql(self, expression: exp.Partition) -> str:
    747        return f"PARTITION({self.expressions(expression)})"

def properties_sql(self, expression: sqlglot.expressions.Properties) -> str: View Source

    749    def properties_sql(self, expression: exp.Properties) -> str:
    750        root_properties = []
    751        with_properties = []
    752
    753        for p in expression.expressions:
    754            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    755            if p_loc == exp.Properties.Location.POST_WITH:
    756                with_properties.append(p)
    757            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    758                root_properties.append(p)
    759
    760        return self.root_properties(
    761            exp.Properties(expressions=root_properties)
    762        ) + self.with_properties(exp.Properties(expressions=with_properties))

def root_properties(self, properties: sqlglot.expressions.Properties) -> str: View Source

    764    def root_properties(self, properties: exp.Properties) -> str:
    765        if properties.expressions:
    766            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    767        return ""

def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str: View Source

    769    def properties(
    770        self,
    771        properties: exp.Properties,
    772        prefix: str = "",
    773        sep: str = ", ",
    774        suffix: str = "",
    775        wrapped: bool = True,
    776    ) -> str:
    777        if properties.expressions:
    778            expressions = self.expressions(properties, sep=sep, indent=False)
    779            expressions = self.wrap(expressions) if wrapped else expressions
    780            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
    781        return ""

def with_properties(self, properties: sqlglot.expressions.Properties) -> str: View Source

    783    def with_properties(self, properties: exp.Properties) -> str:
    784        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]]: View Source

    786    def locate_properties(
    787        self, properties: exp.Properties
    788    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
    789        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
    790            key: [] for key in exp.Properties.Location
    791        }
    792
    793        for p in properties.expressions:
    794            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    795            if p_loc == exp.Properties.Location.POST_NAME:
    796                properties_locs[exp.Properties.Location.POST_NAME].append(p)
    797            elif p_loc == exp.Properties.Location.POST_INDEX:
    798                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
    799            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    800                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
    801            elif p_loc == exp.Properties.Location.POST_WITH:
    802                properties_locs[exp.Properties.Location.POST_WITH].append(p)
    803            elif p_loc == exp.Properties.Location.POST_CREATE:
    804                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
    805            elif p_loc == exp.Properties.Location.POST_ALIAS:
    806                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
    807            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
    808                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
    809            elif p_loc == exp.Properties.Location.UNSUPPORTED:
    810                self.unsupported(f"Unsupported property {p.key}")
    811
    812        return properties_locs

def property_sql(self, expression: sqlglot.expressions.Property) -> str: View Source

    814    def property_sql(self, expression: exp.Property) -> str:
    815        property_cls = expression.__class__
    816        if property_cls == exp.Property:
    817            return f"{expression.name}={self.sql(expression, 'value')}"
    818
    819        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    820        if not property_name:
    821            self.unsupported(f"Unsupported property {expression.key}")
    822
    823        return f"{property_name}={self.sql(expression, 'this')}"

def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str: View Source

    825    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    826        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    827        options = f" {options}" if options else ""
    828        return f"LIKE {self.sql(expression, 'this')}{options}"

def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str: View Source

    830    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    831        no = "NO " if expression.args.get("no") else ""
    832        protection = " PROTECTION" if expression.args.get("protection") else ""
    833        return f"{no}FALLBACK{protection}"

def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str: View Source

    835    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    836        no = "NO " if expression.args.get("no") else ""
    837        dual = "DUAL " if expression.args.get("dual") else ""
    838        before = "BEFORE " if expression.args.get("before") else ""
    839        return f"{no}{dual}{before}JOURNAL"

def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str: View Source

    841    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    842        freespace = self.sql(expression, "this")
    843        percent = " PERCENT" if expression.args.get("percent") else ""
    844        return f"FREESPACE={freespace}{percent}"

def afterjournalproperty_sql(self, expression: sqlglot.expressions.AfterJournalProperty) -> str: View Source

    846    def afterjournalproperty_sql(self, expression: exp.AfterJournalProperty) -> str:
    847        no = "NO " if expression.args.get("no") else ""
    848        dual = "DUAL " if expression.args.get("dual") else ""
    849        local = ""
    850        if expression.args.get("local") is not None:
    851            local = "LOCAL " if expression.args.get("local") else "NOT LOCAL "
    852        return f"{no}{dual}{local}AFTER JOURNAL"

def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str: View Source

    854    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    855        if expression.args.get("default"):
    856            property = "DEFAULT"
    857        elif expression.args.get("on"):
    858            property = "ON"
    859        else:
    860            property = "OFF"
    861        return f"CHECKSUM={property}"

def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str: View Source

    863    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    864        if expression.args.get("no"):
    865            return "NO MERGEBLOCKRATIO"
    866        if expression.args.get("default"):
    867            return "DEFAULT MERGEBLOCKRATIO"
    868
    869        percent = " PERCENT" if expression.args.get("percent") else ""
    870        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str: View Source

    872    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    873        default = expression.args.get("default")
    874        min = expression.args.get("min")
    875        if default is not None or min is not None:
    876            if default:
    877                property = "DEFAULT"
    878            elif min:
    879                property = "MINIMUM"
    880            else:
    881                property = "MAXIMUM"
    882            return f"{property} DATABLOCKSIZE"
    883        else:
    884            units = expression.args.get("units")
    885            units = f" {units}" if units else ""
    886            return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str: View Source

    888    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    889        autotemp = expression.args.get("autotemp")
    890        always = expression.args.get("always")
    891        default = expression.args.get("default")
    892        manual = expression.args.get("manual")
    893        never = expression.args.get("never")
    894
    895        if autotemp is not None:
    896            property = f"AUTOTEMP({self.expressions(autotemp)})"
    897        elif always:
    898            property = "ALWAYS"
    899        elif default:
    900            property = "DEFAULT"
    901        elif manual:
    902            property = "MANUAL"
    903        elif never:
    904            property = "NEVER"
    905        return f"BLOCKCOMPRESSION={property}"

def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str: View Source

    907    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    908        no = expression.args.get("no")
    909        no = " NO" if no else ""
    910        concurrent = expression.args.get("concurrent")
    911        concurrent = " CONCURRENT" if concurrent else ""
    912
    913        for_ = ""
    914        if expression.args.get("for_all"):
    915            for_ = " FOR ALL"
    916        elif expression.args.get("for_insert"):
    917            for_ = " FOR INSERT"
    918        elif expression.args.get("for_none"):
    919            for_ = " FOR NONE"
    920        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str: View Source

    922    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    923        kind = expression.args.get("kind")
    924        this: str = f" {this}" if expression.this else ""
    925        for_or_in = expression.args.get("for_or_in")
    926        lock_type = expression.args.get("lock_type")
    927        override = " OVERRIDE" if expression.args.get("override") else ""
    928        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"

def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str: View Source

    930    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    931        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    932        statistics = expression.args.get("statistics")
    933        statistics_sql = ""
    934        if statistics is not None:
    935            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    936        return f"{data_sql}{statistics_sql}"

def insert_sql(self, expression: sqlglot.expressions.Insert) -> str: View Source

    938    def insert_sql(self, expression: exp.Insert) -> str:
    939        overwrite = expression.args.get("overwrite")
    940
    941        if isinstance(expression.this, exp.Directory):
    942            this = "OVERWRITE " if overwrite else "INTO "
    943        else:
    944            this = "OVERWRITE TABLE " if overwrite else "INTO "
    945
    946        alternative = expression.args.get("alternative")
    947        alternative = f" OR {alternative} " if alternative else " "
    948        this = f"{this}{self.sql(expression, 'this')}"
    949
    950        exists = " IF EXISTS " if expression.args.get("exists") else " "
    951        partition_sql = (
    952            self.sql(expression, "partition") if expression.args.get("partition") else ""
    953        )
    954        expression_sql = self.sql(expression, "expression")
    955        returning = self.sql(expression, "returning")
    956        sep = self.sep() if partition_sql else ""
    957        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{returning}"
    958        return self.prepend_ctes(expression, sql)

def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str: View Source

    960    def intersect_sql(self, expression: exp.Intersect) -> str:
    961        return self.prepend_ctes(
    962            expression,
    963            self.set_operation(expression, self.intersect_op(expression)),
    964        )

def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str: View Source

    966    def intersect_op(self, expression: exp.Intersect) -> str:
    967        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"

def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str: View Source

    969    def introducer_sql(self, expression: exp.Introducer) -> str:
    970        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"

def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str: View Source

    972    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    973        return expression.name.upper()

def returning_sql(self, expression: sqlglot.expressions.Returning) -> str: View Source

    975    def returning_sql(self, expression: exp.Returning) -> str:
    976        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"

def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str: View Source

    978    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    979        fields = expression.args.get("fields")
    980        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    981        escaped = expression.args.get("escaped")
    982        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    983        items = expression.args.get("collection_items")
    984        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    985        keys = expression.args.get("map_keys")
    986        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    987        lines = expression.args.get("lines")
    988        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    989        null = expression.args.get("null")
    990        null = f" NULL DEFINED AS {null}" if null else ""
    991        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str: View Source

     993    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
     994        table = ".".join(
     995            part
     996            for part in [
     997                self.sql(expression, "catalog"),
     998                self.sql(expression, "db"),
     999                self.sql(expression, "this"),
    1000            ]
    1001            if part
    1002        )
    1003
    1004        alias = self.sql(expression, "alias")
    1005        alias = f"{sep}{alias}" if alias else ""
    1006        hints = self.expressions(expression, key="hints", sep=", ", flat=True)
    1007        hints = f" WITH ({hints})" if hints else ""
    1008        laterals = self.expressions(expression, key="laterals", sep="")
    1009        joins = self.expressions(expression, key="joins", sep="")
    1010        pivots = self.expressions(expression, key="pivots", sep="")
    1011        system_time = expression.args.get("system_time")
    1012        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
    1013
    1014        return f"{table}{system_time}{alias}{hints}{laterals}{joins}{pivots}"

def tablesample_sql( self, expression: sqlglot.expressions.TableSample, seed_prefix: str = 'SEED') -> str: View Source

    1016    def tablesample_sql(self, expression: exp.TableSample, seed_prefix: str = "SEED") -> str:
    1017        if self.alias_post_tablesample and expression.this.alias:
    1018            this = self.sql(expression.this, "this")
    1019            alias = f" AS {self.sql(expression.this, 'alias')}"
    1020        else:
    1021            this = self.sql(expression, "this")
    1022            alias = ""
    1023        method = self.sql(expression, "method")
    1024        method = f"{method.upper()} " if method else ""
    1025        numerator = self.sql(expression, "bucket_numerator")
    1026        denominator = self.sql(expression, "bucket_denominator")
    1027        field = self.sql(expression, "bucket_field")
    1028        field = f" ON {field}" if field else ""
    1029        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1030        percent = self.sql(expression, "percent")
    1031        percent = f"{percent} PERCENT" if percent else ""
    1032        rows = self.sql(expression, "rows")
    1033        rows = f"{rows} ROWS" if rows else ""
    1034        size = self.sql(expression, "size")
    1035        seed = self.sql(expression, "seed")
    1036        seed = f" {seed_prefix} ({seed})" if seed else ""
    1037        kind = expression.args.get("kind", "TABLESAMPLE")
    1038        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"

def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str: View Source

    1040    def pivot_sql(self, expression: exp.Pivot) -> str:
    1041        this = self.sql(expression, "this")
    1042        alias = self.sql(expression, "alias")
    1043        alias = f" AS {alias}" if alias else ""
    1044        unpivot = expression.args.get("unpivot")
    1045        direction = "UNPIVOT" if unpivot else "PIVOT"
    1046        expressions = self.expressions(expression, key="expressions")
    1047        field = self.sql(expression, "field")
    1048        return f"{this} {direction}({expressions} FOR {field}){alias}"

def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str: View Source

    1050    def tuple_sql(self, expression: exp.Tuple) -> str:
    1051        return f"({self.expressions(expression, flat=True)})"

def update_sql(self, expression: sqlglot.expressions.Update) -> str: View Source

    1053    def update_sql(self, expression: exp.Update) -> str:
    1054        this = self.sql(expression, "this")
    1055        set_sql = self.expressions(expression, flat=True)
    1056        from_sql = self.sql(expression, "from")
    1057        where_sql = self.sql(expression, "where")
    1058        returning = self.sql(expression, "returning")
    1059        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
    1060        return self.prepend_ctes(expression, sql)

def values_sql(self, expression: sqlglot.expressions.Values) -> str: View Source

    1062    def values_sql(self, expression: exp.Values) -> str:
    1063        args = self.expressions(expression)
    1064        alias = self.sql(expression, "alias")
    1065        values = f"VALUES{self.seg('')}{args}"
    1066        values = (
    1067            f"({values})"
    1068            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1069            else values
    1070        )
    1071        return f"{values} AS {alias}" if alias else values

def var_sql(self, expression: sqlglot.expressions.Var) -> str: View Source

    1073    def var_sql(self, expression: exp.Var) -> str:
    1074        return self.sql(expression, "this")

def into_sql(self, expression: sqlglot.expressions.Into) -> str: View Source

    1076    def into_sql(self, expression: exp.Into) -> str:
    1077        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1078        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    1079        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"

def from_sql(self, expression: sqlglot.expressions.From) -> str: View Source

    1081    def from_sql(self, expression: exp.From) -> str:
    1082        expressions = self.expressions(expression, flat=True)
    1083        return f"{self.seg('FROM')} {expressions}"

def group_sql(self, expression: sqlglot.expressions.Group) -> str: View Source

    1085    def group_sql(self, expression: exp.Group) -> str:
    1086        group_by = self.op_expressions("GROUP BY", expression)
    1087        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1088        grouping_sets = (
    1089            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1090        )
    1091
    1092        cube = expression.args.get("cube", [])
    1093        if seq_get(cube, 0) is True:
    1094            return f"{group_by}{self.seg('WITH CUBE')}"
    1095        else:
    1096            cube_sql = self.expressions(expression, key="cube", indent=False)
    1097            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1098
    1099        rollup = expression.args.get("rollup", [])
    1100        if seq_get(rollup, 0) is True:
    1101            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1102        else:
    1103            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1104            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1105
    1106        groupings = csv(grouping_sets, cube_sql, rollup_sql, sep=",")
    1107
    1108        if expression.args.get("expressions") and groupings:
    1109            group_by = f"{group_by},"
    1110
    1111        return f"{group_by}{groupings}"

def having_sql(self, expression: sqlglot.expressions.Having) -> str: View Source

    1113    def having_sql(self, expression: exp.Having) -> str:
    1114        this = self.indent(self.sql(expression, "this"))
    1115        return f"{self.seg('HAVING')}{self.sep()}{this}"

def join_sql(self, expression: sqlglot.expressions.Join) -> str: View Source

    1117    def join_sql(self, expression: exp.Join) -> str:
    1118        op_sql = self.seg(
    1119            " ".join(
    1120                op
    1121                for op in (
    1122                    "NATURAL" if expression.args.get("natural") else None,
    1123                    expression.side,
    1124                    expression.kind,
    1125                    "JOIN",
    1126                )
    1127                if op
    1128            )
    1129        )
    1130        on_sql = self.sql(expression, "on")
    1131        using = expression.args.get("using")
    1132
    1133        if not on_sql and using:
    1134            on_sql = csv(*(self.sql(column) for column in using))
    1135
    1136        if on_sql:
    1137            on_sql = self.indent(on_sql, skip_first=True)
    1138            space = self.seg(" " * self.pad) if self.pretty else " "
    1139            if using:
    1140                on_sql = f"{space}USING ({on_sql})"
    1141            else:
    1142                on_sql = f"{space}ON {on_sql}"
    1143
    1144        expression_sql = self.sql(expression, "expression")
    1145        this_sql = self.sql(expression, "this")
    1146        return f"{expression_sql}{op_sql} {this_sql}{on_sql}"

def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str: View Source

    1148    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    1149        args = self.expressions(expression, flat=True)
    1150        args = f"({args})" if len(args.split(",")) > 1 else args
    1151        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"

def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str: View Source

    1153    def lateral_sql(self, expression: exp.Lateral) -> str:
    1154        this = self.sql(expression, "this")
    1155
    1156        if isinstance(expression.this, exp.Subquery):
    1157            return f"LATERAL {this}"
    1158
    1159        if expression.args.get("view"):
    1160            alias = expression.args["alias"]
    1161            columns = self.expressions(alias, key="columns", flat=True)
    1162            table = f" {alias.name}" if alias.name else ""
    1163            columns = f" AS {columns}" if columns else ""
    1164            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1165            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1166
    1167        alias = self.sql(expression, "alias")
    1168        alias = f" AS {alias}" if alias else ""
    1169        return f"LATERAL {this}{alias}"

def limit_sql(self, expression: sqlglot.expressions.Limit) -> str: View Source

    1171    def limit_sql(self, expression: exp.Limit) -> str:
    1172        this = self.sql(expression, "this")
    1173        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"

def offset_sql(self, expression: sqlglot.expressions.Offset) -> str: View Source

    1175    def offset_sql(self, expression: exp.Offset) -> str:
    1176        this = self.sql(expression, "this")
    1177        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"

def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str: View Source

    1179    def setitem_sql(self, expression: exp.SetItem) -> str:
    1180        kind = self.sql(expression, "kind")
    1181        kind = f"{kind} " if kind else ""
    1182        this = self.sql(expression, "this")
    1183        expressions = self.expressions(expression)
    1184        collate = self.sql(expression, "collate")
    1185        collate = f" COLLATE {collate}" if collate else ""
    1186        global_ = "GLOBAL " if expression.args.get("global") else ""
    1187        return f"{global_}{kind}{this}{expressions}{collate}"

def set_sql(self, expression: sqlglot.expressions.Set) -> str: View Source

    1189    def set_sql(self, expression: exp.Set) -> str:
    1190        expressions = (
    1191            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1192        )
    1193        return f"SET{expressions}"

def lock_sql(self, expression: sqlglot.expressions.Lock) -> str: View Source

    1195    def lock_sql(self, expression: exp.Lock) -> str:
    1196        if self.LOCKING_READS_SUPPORTED:
    1197            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
    1198            return self.seg(f"FOR {lock_type}")
    1199
    1200        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1201        return ""

def literal_sql(self, expression: sqlglot.expressions.Literal) -> str: View Source

    1203    def literal_sql(self, expression: exp.Literal) -> str:
    1204        text = expression.this or ""
    1205        if expression.is_string:
    1206            text = text.replace(self.quote_end, self._escaped_quote_end)
    1207            if self.pretty:
    1208                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1209            text = f"{self.quote_start}{text}{self.quote_end}"
    1210        return text

def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str: View Source

    1212    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1213        local = " LOCAL" if expression.args.get("local") else ""
    1214        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1215        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1216        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1217        partition = self.sql(expression, "partition")
    1218        partition = f" {partition}" if partition else ""
    1219        input_format = self.sql(expression, "input_format")
    1220        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1221        serde = self.sql(expression, "serde")
    1222        serde = f" SERDE {serde}" if serde else ""
    1223        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

def null_sql(self, *_) -> str: View Source

    1225    def null_sql(self, *_) -> str:
    1226        return "NULL"

def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str: View Source

    1228    def boolean_sql(self, expression: exp.Boolean) -> str:
    1229        return "TRUE" if expression.this else "FALSE"

def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str: View Source

    1231    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1232        this = self.sql(expression, "this")
    1233        this = f"{this} " if this else this
    1234        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore

def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str: View Source

    1236    def cluster_sql(self, expression: exp.Cluster) -> str:
    1237        return self.op_expressions("CLUSTER BY", expression)

def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str: View Source

    1239    def distribute_sql(self, expression: exp.Distribute) -> str:
    1240        return self.op_expressions("DISTRIBUTE BY", expression)

def sort_sql(self, expression: sqlglot.expressions.Sort) -> str: View Source

    1242    def sort_sql(self, expression: exp.Sort) -> str:
    1243        return self.op_expressions("SORT BY", expression)

def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str: View Source

    1245    def ordered_sql(self, expression: exp.Ordered) -> str:
    1246        desc = expression.args.get("desc")
    1247        asc = not desc
    1248
    1249        nulls_first = expression.args.get("nulls_first")
    1250        nulls_last = not nulls_first
    1251        nulls_are_large = self.null_ordering == "nulls_are_large"
    1252        nulls_are_small = self.null_ordering == "nulls_are_small"
    1253        nulls_are_last = self.null_ordering == "nulls_are_last"
    1254
    1255        sort_order = " DESC" if desc else ""
    1256        nulls_sort_change = ""
    1257        if nulls_first and (
    1258            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1259        ):
    1260            nulls_sort_change = " NULLS FIRST"
    1261        elif (
    1262            nulls_last
    1263            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1264            and not nulls_are_last
    1265        ):
    1266            nulls_sort_change = " NULLS LAST"
    1267
    1268        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1269            self.unsupported(
    1270                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1271            )
    1272            nulls_sort_change = ""
    1273
    1274        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str: View Source

    1276    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1277        partition = self.partition_by_sql(expression)
    1278        order = self.sql(expression, "order")
    1279        measures = self.sql(expression, "measures")
    1280        measures = self.seg(f"MEASURES {measures}") if measures else ""
    1281        rows = self.sql(expression, "rows")
    1282        rows = self.seg(rows) if rows else ""
    1283        after = self.sql(expression, "after")
    1284        after = self.seg(after) if after else ""
    1285        pattern = self.sql(expression, "pattern")
    1286        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1287        define = self.sql(expression, "define")
    1288        define = self.seg(f"DEFINE {define}") if define else ""
    1289        body = "".join(
    1290            (
    1291                partition,
    1292                order,
    1293                measures,
    1294                rows,
    1295                after,
    1296                pattern,
    1297                define,
    1298            )
    1299        )
    1300        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"

def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str: View Source

    1302    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1303        return csv(
    1304            *sqls,
    1305            *[self.sql(sql) for sql in expression.args.get("joins") or []],
    1306            self.sql(expression, "match"),
    1307            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
    1308            self.sql(expression, "where"),
    1309            self.sql(expression, "group"),
    1310            self.sql(expression, "having"),
    1311            self.sql(expression, "qualify"),
    1312            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
    1313            if expression.args.get("windows")
    1314            else "",
    1315            self.sql(expression, "distribute"),
    1316            self.sql(expression, "sort"),
    1317            self.sql(expression, "cluster"),
    1318            self.sql(expression, "order"),
    1319            self.sql(expression, "limit"),
    1320            self.sql(expression, "offset"),
    1321            self.sql(expression, "lock"),
    1322            self.sql(expression, "sample"),
    1323            sep="",
    1324        )

def select_sql(self, expression: sqlglot.expressions.Select) -> str: View Source

    1326    def select_sql(self, expression: exp.Select) -> str:
    1327        hint = self.sql(expression, "hint")
    1328        distinct = self.sql(expression, "distinct")
    1329        distinct = f" {distinct}" if distinct else ""
    1330        expressions = self.expressions(expression)
    1331        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1332        sql = self.query_modifiers(
    1333            expression,
    1334            f"SELECT{hint}{distinct}{expressions}",
    1335            self.sql(expression, "into", comment=False),
    1336            self.sql(expression, "from", comment=False),
    1337        )
    1338        return self.prepend_ctes(expression, sql)

def schema_sql(self, expression: sqlglot.expressions.Schema) -> str: View Source

    1340    def schema_sql(self, expression: exp.Schema) -> str:
    1341        this = self.sql(expression, "this")
    1342        this = f"{this} " if this else ""
    1343        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1344        return f"{this}{sql}"

def star_sql(self, expression: sqlglot.expressions.Star) -> str: View Source

    1346    def star_sql(self, expression: exp.Star) -> str:
    1347        except_ = self.expressions(expression, key="except", flat=True)
    1348        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1349        replace = self.expressions(expression, key="replace", flat=True)
    1350        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1351        return f"*{except_}{replace}"

def structkwarg_sql(self, expression: sqlglot.expressions.StructKwarg) -> str: View Source

    1353    def structkwarg_sql(self, expression: exp.StructKwarg) -> str:
    1354        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"

def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str: View Source

    1356    def parameter_sql(self, expression: exp.Parameter) -> str:
    1357        this = self.sql(expression, "this")
    1358        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1359        return f"{self.PARAMETER_TOKEN}{this}"

def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str: View Source

    1361    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1362        this = self.sql(expression, "this")
    1363        kind = expression.text("kind")
    1364        if kind:
    1365            kind = f"{kind}."
    1366        return f"@@{kind}{this}"

def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str: View Source

    1368    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    1369        return f":{expression.name}" if expression.name else "?"

def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str: View Source

    1371    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1372        alias = self.sql(expression, "alias")
    1373        alias = f"{sep}{alias}" if alias else ""
    1374
    1375        sql = self.query_modifiers(
    1376            expression,
    1377            self.wrap(expression),
    1378            alias,
    1379            self.expressions(expression, key="pivots", sep=" "),
    1380        )
    1381
    1382        return self.prepend_ctes(expression, sql)

def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str: View Source

    1384    def qualify_sql(self, expression: exp.Qualify) -> str:
    1385        this = self.indent(self.sql(expression, "this"))
    1386        return f"{self.seg('QUALIFY')}{self.sep()}{this}"

def union_sql(self, expression: sqlglot.expressions.Union) -> str: View Source

    1388    def union_sql(self, expression: exp.Union) -> str:
    1389        return self.prepend_ctes(
    1390            expression,
    1391            self.set_operation(expression, self.union_op(expression)),
    1392        )

def union_op(self, expression: sqlglot.expressions.Union) -> str: View Source

    1394    def union_op(self, expression: exp.Union) -> str:
    1395        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1396        kind = kind if expression.args.get("distinct") else " ALL"
    1397        return f"UNION{kind}"

def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str: View Source

    1399    def unnest_sql(self, expression: exp.Unnest) -> str:
    1400        args = self.expressions(expression, flat=True)
    1401        alias = expression.args.get("alias")
    1402        if alias and self.unnest_column_only:
    1403            columns = alias.columns
    1404            alias = self.sql(columns[0]) if columns else ""
    1405        else:
    1406            alias = self.sql(expression, "alias")
    1407        alias = f" AS {alias}" if alias else alias
    1408        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1409        offset = expression.args.get("offset")
    1410        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1411        return f"UNNEST({args}){ordinality}{alias}{offset}"

def where_sql(self, expression: sqlglot.expressions.Where) -> str: View Source

    1413    def where_sql(self, expression: exp.Where) -> str:
    1414        this = self.indent(self.sql(expression, "this"))
    1415        return f"{self.seg('WHERE')}{self.sep()}{this}"

def window_sql(self, expression: sqlglot.expressions.Window) -> str: View Source

    1417    def window_sql(self, expression: exp.Window) -> str:
    1418        this = self.sql(expression, "this")
    1419
    1420        partition = self.partition_by_sql(expression)
    1421
    1422        order = expression.args.get("order")
    1423        order_sql = self.order_sql(order, flat=True) if order else ""
    1424
    1425        partition_sql = partition + " " if partition and order else partition
    1426
    1427        spec = expression.args.get("spec")
    1428        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
    1429
    1430        alias = self.sql(expression, "alias")
    1431        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
    1432
    1433        if not partition and not order and not spec and alias:
    1434            return f"{this} {alias}"
    1435
    1436        window_args = alias + partition_sql + order_sql + spec_sql
    1437
    1438        return f"{this} ({window_args.strip()})"

def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str: View Source

    1440    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    1441        partition = self.expressions(expression, key="partition_by", flat=True)
    1442        return f"PARTITION BY {partition}" if partition else ""

def window_spec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str: View Source

    1444    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
    1445        kind = self.sql(expression, "kind")
    1446        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1447        end = (
    1448            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1449            or "CURRENT ROW"
    1450        )
    1451        return f"{kind} BETWEEN {start} AND {end}"

def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str: View Source

    1453    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    1454        this = self.sql(expression, "this")
    1455        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    1456        return f"{this} WITHIN GROUP ({expression_sql})"

def between_sql(self, expression: sqlglot.expressions.Between) -> str: View Source

    1458    def between_sql(self, expression: exp.Between) -> str:
    1459        this = self.sql(expression, "this")
    1460        low = self.sql(expression, "low")
    1461        high = self.sql(expression, "high")
    1462        return f"{this} BETWEEN {low} AND {high}"

def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str: View Source

    1464    def bracket_sql(self, expression: exp.Bracket) -> str:
    1465        expressions = apply_index_offset(expression.expressions, self.index_offset)
    1466        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1467
    1468        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

def all_sql(self, expression: sqlglot.expressions.All) -> str: View Source

    1470    def all_sql(self, expression: exp.All) -> str:
    1471        return f"ALL {self.wrap(expression)}"

def any_sql(self, expression: sqlglot.expressions.Any) -> str: View Source

    1473    def any_sql(self, expression: exp.Any) -> str:
    1474        this = self.sql(expression, "this")
    1475        if isinstance(expression.this, exp.Subqueryable):
    1476            this = self.wrap(this)
    1477        return f"ANY {this}"

def exists_sql(self, expression: sqlglot.expressions.Exists) -> str: View Source

    1479    def exists_sql(self, expression: exp.Exists) -> str:
    1480        return f"EXISTS{self.wrap(expression)}"

def case_sql(self, expression: sqlglot.expressions.Case) -> str: View Source

    1482    def case_sql(self, expression: exp.Case) -> str:
    1483        this = self.sql(expression, "this")
    1484        statements = [f"CASE {this}" if this else "CASE"]
    1485
    1486        for e in expression.args["ifs"]:
    1487            statements.append(f"WHEN {self.sql(e, 'this')}")
    1488            statements.append(f"THEN {self.sql(e, 'true')}")
    1489
    1490        default = self.sql(expression, "default")
    1491
    1492        if default:
    1493            statements.append(f"ELSE {default}")
    1494
    1495        statements.append("END")
    1496
    1497        if self.pretty and self.text_width(statements) > self._max_text_width:
    1498            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1499
    1500        return " ".join(statements)

def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str: View Source

    1502    def constraint_sql(self, expression: exp.Constraint) -> str:
    1503        this = self.sql(expression, "this")
    1504        expressions = self.expressions(expression, flat=True)
    1505        return f"CONSTRAINT {this} {expressions}"

def extract_sql(self, expression: sqlglot.expressions.Extract) -> str: View Source

    1507    def extract_sql(self, expression: exp.Extract) -> str:
    1508        this = self.sql(expression, "this")
    1509        expression_sql = self.sql(expression, "expression")
    1510        return f"EXTRACT({this} FROM {expression_sql})"

def trim_sql(self, expression: sqlglot.expressions.Trim) -> str: View Source

    1512    def trim_sql(self, expression: exp.Trim) -> str:
    1513        trim_type = self.sql(expression, "position")
    1514
    1515        if trim_type == "LEADING":
    1516            return self.func("LTRIM", expression.this)
    1517        elif trim_type == "TRAILING":
    1518            return self.func("RTRIM", expression.this)
    1519        else:
    1520            return self.func("TRIM", expression.this, expression.expression)

def concat_sql(self, expression: sqlglot.expressions.Concat) -> str: View Source

    1522    def concat_sql(self, expression: exp.Concat) -> str:
    1523        if len(expression.expressions) == 1:
    1524            return self.sql(expression.expressions[0])
    1525        return self.function_fallback_sql(expression)

def check_sql(self, expression: sqlglot.expressions.Check) -> str: View Source

    1527    def check_sql(self, expression: exp.Check) -> str:
    1528        this = self.sql(expression, key="this")
    1529        return f"CHECK ({this})"

def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str: View Source

    1531    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1532        expressions = self.expressions(expression, flat=True)
    1533        reference = self.sql(expression, "reference")
    1534        reference = f" {reference}" if reference else ""
    1535        delete = self.sql(expression, "delete")
    1536        delete = f" ON DELETE {delete}" if delete else ""
    1537        update = self.sql(expression, "update")
    1538        update = f" ON UPDATE {update}" if update else ""
    1539        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str: View Source

    1541    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1542        expressions = self.expressions(expression, flat=True)
    1543        options = self.expressions(expression, "options", flat=True, sep=" ")
    1544        options = f" {options}" if options else ""
    1545        return f"PRIMARY KEY ({expressions}){options}"

def unique_sql(self, expression: sqlglot.expressions.Unique) -> str: View Source

    1547    def unique_sql(self, expression: exp.Unique) -> str:
    1548        columns = self.expressions(expression, key="expressions")
    1549        return f"UNIQUE ({columns})"

def if_sql(self, expression: sqlglot.expressions.If) -> str: View Source

    1551    def if_sql(self, expression: exp.If) -> str:
    1552        return self.case_sql(
    1553            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1554        )

def in_sql(self, expression: sqlglot.expressions.In) -> str: View Source

    1556    def in_sql(self, expression: exp.In) -> str:
    1557        query = expression.args.get("query")
    1558        unnest = expression.args.get("unnest")
    1559        field = expression.args.get("field")
    1560        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1561
    1562        if query:
    1563            in_sql = self.wrap(query)
    1564        elif unnest:
    1565            in_sql = self.in_unnest_op(unnest)
    1566        elif field:
    1567            in_sql = self.sql(field)
    1568        else:
    1569            in_sql = f"({self.expressions(expression, flat=True)})"
    1570
    1571        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str: View Source

    1573    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    1574        return f"(SELECT {self.sql(unnest)})"

def interval_sql(self, expression: sqlglot.expressions.Interval) -> str: View Source

    1576    def interval_sql(self, expression: exp.Interval) -> str:
    1577        this = expression.args.get("this")
    1578        if this:
    1579            this = (
    1580                f" {this}"
    1581                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
    1582                else f" ({this})"
    1583            )
    1584        else:
    1585            this = ""
    1586        unit = self.sql(expression, "unit")
    1587        unit = f" {unit}" if unit else ""
    1588        return f"INTERVAL{this}{unit}"

def return_sql(self, expression: sqlglot.expressions.Return) -> str: View Source

    1590    def return_sql(self, expression: exp.Return) -> str:
    1591        return f"RETURN {self.sql(expression, 'this')}"

def reference_sql(self, expression: sqlglot.expressions.Reference) -> str: View Source

    1593    def reference_sql(self, expression: exp.Reference) -> str:
    1594        this = self.sql(expression, "this")
    1595        expressions = self.expressions(expression, flat=True)
    1596        expressions = f"({expressions})" if expressions else ""
    1597        options = self.expressions(expression, "options", flat=True, sep=" ")
    1598        options = f" {options}" if options else ""
    1599        return f"REFERENCES {this}{expressions}{options}"

def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str: View Source

    1601    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    1602        return self.func(expression.name, *expression.expressions)

def paren_sql(self, expression: sqlglot.expressions.Paren) -> str: View Source

    1604    def paren_sql(self, expression: exp.Paren) -> str:
    1605        if isinstance(expression.unnest(), exp.Select):
    1606            sql = self.wrap(expression)
    1607        else:
    1608            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1609            sql = f"({sql}{self.seg(')', sep='')}"
    1610
    1611        return self.prepend_ctes(expression, sql)

def neg_sql(self, expression: sqlglot.expressions.Neg) -> str: View Source

    1613    def neg_sql(self, expression: exp.Neg) -> str:
    1614        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    1615        this_sql = self.sql(expression, "this")
    1616        sep = " " if this_sql[0] == "-" else ""
    1617        return f"-{sep}{this_sql}"

def not_sql(self, expression: sqlglot.expressions.Not) -> str: View Source

    1619    def not_sql(self, expression: exp.Not) -> str:
    1620        return f"NOT {self.sql(expression, 'this')}"

def alias_sql(self, expression: sqlglot.expressions.Alias) -> str: View Source

    1622    def alias_sql(self, expression: exp.Alias) -> str:
    1623        to_sql = self.sql(expression, "alias")
    1624        to_sql = f" AS {to_sql}" if to_sql else ""
    1625        return f"{self.sql(expression, 'this')}{to_sql}"

def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str: View Source

    1627    def aliases_sql(self, expression: exp.Aliases) -> str:
    1628        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"

def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str: View Source

    1630    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1631        this = self.sql(expression, "this")
    1632        zone = self.sql(expression, "zone")
    1633        return f"{this} AT TIME ZONE {zone}"

def add_sql(self, expression: sqlglot.expressions.Add) -> str: View Source

    1635    def add_sql(self, expression: exp.Add) -> str:
    1636        return self.binary(expression, "+")

def and_sql(self, expression: sqlglot.expressions.And) -> str: View Source

    1638    def and_sql(self, expression: exp.And) -> str:
    1639        return self.connector_sql(expression, "AND")

def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str: View Source

    1641    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1642        if not self.pretty:
    1643            return self.binary(expression, op)
    1644
    1645        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
    1646        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1647        return f"{sep}{op} ".join(sqls)

def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str: View Source

    1649    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    1650        return self.binary(expression, "&")

def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str: View Source

    1652    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    1653        return self.binary(expression, "<<")

def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str: View Source

    1655    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    1656        return f"~{self.sql(expression, 'this')}"

def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str: View Source

    1658    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    1659        return self.binary(expression, "|")

def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str: View Source

    1661    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    1662        return self.binary(expression, ">>")

def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str: View Source

    1664    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    1665        return self.binary(expression, "^")

def cast_sql(self, expression: sqlglot.expressions.Cast) -> str: View Source

    1667    def cast_sql(self, expression: exp.Cast) -> str:
    1668        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"

def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str: View Source

    1670    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    1671        zone = self.sql(expression, "this")
    1672        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"

def collate_sql(self, expression: sqlglot.expressions.Collate) -> str: View Source

    1674    def collate_sql(self, expression: exp.Collate) -> str:
    1675        return self.binary(expression, "COLLATE")

def command_sql(self, expression: sqlglot.expressions.Command) -> str: View Source

    1677    def command_sql(self, expression: exp.Command) -> str:
    1678        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"

def comment_sql(self, expression: sqlglot.expressions.Comment) -> str: View Source

    1680    def comment_sql(self, expression: exp.Comment) -> str:
    1681        this = self.sql(expression, "this")
    1682        kind = expression.args["kind"]
    1683        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1684        expression_sql = self.sql(expression, "expression")
    1685        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str: View Source

    1687    def transaction_sql(self, expression: exp.Transaction) -> str:
    1688        return "BEGIN"

def commit_sql(self, expression: sqlglot.expressions.Commit) -> str: View Source

    1690    def commit_sql(self, expression: exp.Commit) -> str:
    1691        chain = expression.args.get("chain")
    1692        if chain is not None:
    1693            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    1694
    1695        return f"COMMIT{chain or ''}"

def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str: View Source

    1697    def rollback_sql(self, expression: exp.Rollback) -> str:
    1698        savepoint = expression.args.get("savepoint")
    1699        savepoint = f" TO {savepoint}" if savepoint else ""
    1700        return f"ROLLBACK{savepoint}"

def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str: View Source

    1702    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    1703        this = self.sql(expression, "this")
    1704
    1705        dtype = self.sql(expression, "dtype")
    1706        if dtype:
    1707            collate = self.sql(expression, "collate")
    1708            collate = f" COLLATE {collate}" if collate else ""
    1709            using = self.sql(expression, "using")
    1710            using = f" USING {using}" if using else ""
    1711            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    1712
    1713        default = self.sql(expression, "default")
    1714        if default:
    1715            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    1716
    1717        if not expression.args.get("drop"):
    1718            self.unsupported("Unsupported ALTER COLUMN syntax")
    1719
    1720        return f"ALTER COLUMN {this} DROP DEFAULT"

def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str: View Source

    1722    def renametable_sql(self, expression: exp.RenameTable) -> str:
    1723        this = self.sql(expression, "this")
    1724        return f"RENAME TO {this}"

def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str: View Source

    1726    def altertable_sql(self, expression: exp.AlterTable) -> str:
    1727        actions = expression.args["actions"]
    1728
    1729        if isinstance(actions[0], exp.ColumnDef):
    1730            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
    1731        elif isinstance(actions[0], exp.Schema):
    1732            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
    1733        elif isinstance(actions[0], exp.Delete):
    1734            actions = self.expressions(expression, "actions", flat=True)
    1735        else:
    1736            actions = self.expressions(expression, "actions")
    1737
    1738        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1739        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"

def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str: View Source

    1741    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    1742        expressions = self.expressions(expression)
    1743        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1744        return f"DROP{exists}{expressions}"

def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str: View Source

    1746    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    1747        this = self.sql(expression, "this")
    1748        expression_ = self.sql(expression, "expression")
    1749        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    1750
    1751        enforced = expression.args.get("enforced")
    1752        if enforced is not None:
    1753            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    1754
    1755        return f"{add_constraint} {expression_}"

def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str: View Source

    1757    def distinct_sql(self, expression: exp.Distinct) -> str:
    1758        this = self.expressions(expression, flat=True)
    1759        this = f" {this}" if this else ""
    1760
    1761        on = self.sql(expression, "on")
    1762        on = f" ON {on}" if on else ""
    1763        return f"DISTINCT{this}{on}"

def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str: View Source

    1765    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    1766        return f"{self.sql(expression, 'this')} IGNORE NULLS"

def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str: View Source

    1768    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    1769        return f"{self.sql(expression, 'this')} RESPECT NULLS"

def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str: View Source

    1771    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    1772        return self.sql(
    1773            exp.Cast(
    1774                this=exp.Div(this=expression.this, expression=expression.expression),
    1775                to=exp.DataType(this=exp.DataType.Type.INT),
    1776            )
    1777        )

def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str: View Source

    1779    def dpipe_sql(self, expression: exp.DPipe) -> str:
    1780        return self.binary(expression, "||")

def div_sql(self, expression: sqlglot.expressions.Div) -> str: View Source

    1782    def div_sql(self, expression: exp.Div) -> str:
    1783        return self.binary(expression, "/")

def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str: View Source

    1785    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    1786        return self.binary(expression, "OVERLAPS")

def distance_sql(self, expression: sqlglot.expressions.Distance) -> str: View Source

    1788    def distance_sql(self, expression: exp.Distance) -> str:
    1789        return self.binary(expression, "<->")

def dot_sql(self, expression: sqlglot.expressions.Dot) -> str: View Source

    1791    def dot_sql(self, expression: exp.Dot) -> str:
    1792        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"

def eq_sql(self, expression: sqlglot.expressions.EQ) -> str: View Source

    1794    def eq_sql(self, expression: exp.EQ) -> str:
    1795        return self.binary(expression, "=")

def escape_sql(self, expression: sqlglot.expressions.Escape) -> str: View Source

    1797    def escape_sql(self, expression: exp.Escape) -> str:
    1798        return self.binary(expression, "ESCAPE")

def glob_sql(self, expression: sqlglot.expressions.Glob) -> str: View Source

    1800    def glob_sql(self, expression: exp.Glob) -> str:
    1801        return self.binary(expression, "GLOB")

def gt_sql(self, expression: sqlglot.expressions.GT) -> str: View Source

    1803    def gt_sql(self, expression: exp.GT) -> str:
    1804        return self.binary(expression, ">")

def gte_sql(self, expression: sqlglot.expressions.GTE) -> str: View Source

    1806    def gte_sql(self, expression: exp.GTE) -> str:
    1807        return self.binary(expression, ">=")

def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str: View Source

    1809    def ilike_sql(self, expression: exp.ILike) -> str:
    1810        return self.binary(expression, "ILIKE")

def is_sql(self, expression: sqlglot.expressions.Is) -> str: View Source

    1812    def is_sql(self, expression: exp.Is) -> str:
    1813        return self.binary(expression, "IS")

def like_sql(self, expression: sqlglot.expressions.Like) -> str: View Source

    1815    def like_sql(self, expression: exp.Like) -> str:
    1816        return self.binary(expression, "LIKE")

def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str: View Source

    1818    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    1819        return self.binary(expression, "SIMILAR TO")

def lt_sql(self, expression: sqlglot.expressions.LT) -> str: View Source

    1821    def lt_sql(self, expression: exp.LT) -> str:
    1822        return self.binary(expression, "<")

def lte_sql(self, expression: sqlglot.expressions.LTE) -> str: View Source

    1824    def lte_sql(self, expression: exp.LTE) -> str:
    1825        return self.binary(expression, "<=")

def mod_sql(self, expression: sqlglot.expressions.Mod) -> str: View Source

    1827    def mod_sql(self, expression: exp.Mod) -> str:
    1828        return self.binary(expression, "%")

def mul_sql(self, expression: sqlglot.expressions.Mul) -> str: View Source

    1830    def mul_sql(self, expression: exp.Mul) -> str:
    1831        return self.binary(expression, "*")

def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str: View Source

    1833    def neq_sql(self, expression: exp.NEQ) -> str:
    1834        return self.binary(expression, "<>")

def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str: View Source

    1836    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    1837        return self.binary(expression, "IS NOT DISTINCT FROM")

def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str: View Source

    1839    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    1840        return self.binary(expression, "IS DISTINCT FROM")

def or_sql(self, expression: sqlglot.expressions.Or) -> str: View Source

    1842    def or_sql(self, expression: exp.Or) -> str:
    1843        return self.connector_sql(expression, "OR")

def slice_sql(self, expression: sqlglot.expressions.Slice) -> str: View Source

    1845    def slice_sql(self, expression: exp.Slice) -> str:
    1846        return self.binary(expression, ":")

def sub_sql(self, expression: sqlglot.expressions.Sub) -> str: View Source

    1848    def sub_sql(self, expression: exp.Sub) -> str:
    1849        return self.binary(expression, "-")

def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str: View Source

    1851    def trycast_sql(self, expression: exp.TryCast) -> str:
    1852        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"

def use_sql(self, expression: sqlglot.expressions.Use) -> str: View Source

    1854    def use_sql(self, expression: exp.Use) -> str:
    1855        kind = self.sql(expression, "kind")
    1856        kind = f" {kind}" if kind else ""
    1857        this = self.sql(expression, "this")
    1858        this = f" {this}" if this else ""
    1859        return f"USE{kind}{this}"

def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str: View Source

    1861    def binary(self, expression: exp.Binary, op: str) -> str:
    1862        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"

def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str: View Source

    1864    def function_fallback_sql(self, expression: exp.Func) -> str:
    1865        args = []
    1866        for arg_value in expression.args.values():
    1867            if isinstance(arg_value, list):
    1868                for value in arg_value:
    1869                    args.append(value)
    1870            else:
    1871                args.append(arg_value)
    1872
    1873        return self.func(expression.sql_name(), *args)

def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str: View Source

    1875    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
    1876        return f"{self.normalize_func(name)}({self.format_args(*args)})"

def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str: View Source

    1878    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    1879        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    1880        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
    1881            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    1882        return ", ".join(arg_sqls)

def text_width(self, args: Iterable) -> int: View Source

    1884    def text_width(self, args: t.Iterable) -> int:
    1885        return sum(len(arg) for arg in args)

def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]: View Source

    1887    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    1888        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: View Source

    1890    def expressions(
    1891        self,
    1892        expression: exp.Expression,
    1893        key: t.Optional[str] = None,
    1894        flat: bool = False,
    1895        indent: bool = True,
    1896        sep: str = ", ",
    1897        prefix: str = "",
    1898    ) -> str:
    1899        expressions = expression.args.get(key or "expressions")
    1900
    1901        if not expressions:
    1902            return ""
    1903
    1904        if flat:
    1905            return sep.join(self.sql(e) for e in expressions)
    1906
    1907        num_sqls = len(expressions)
    1908
    1909        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    1910        pad = " " * self.pad
    1911        stripped_sep = sep.strip()
    1912
    1913        result_sqls = []
    1914        for i, e in enumerate(expressions):
    1915            sql = self.sql(e, comment=False)
    1916            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    1917
    1918            if self.pretty:
    1919                if self._leading_comma:
    1920                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    1921                else:
    1922                    result_sqls.append(
    1923                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    1924                    )
    1925            else:
    1926                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    1927
    1928        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    1929        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: View Source

    1931    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    1932        flat = flat or isinstance(expression.parent, exp.Properties)
    1933        expressions_sql = self.expressions(expression, flat=flat)
    1934        if flat:
    1935            return f"{op} {expressions_sql}"
    1936        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

def naked_property(self, expression: sqlglot.expressions.Property) -> str: View Source

    1938    def naked_property(self, expression: exp.Property) -> str:
    1939        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    1940        if not property_name:
    1941            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    1942        return f"{property_name} {self.sql(expression, 'this')}"

def set_operation(self, expression: sqlglot.expressions.Expression, op: str) -> str: View Source

    1944    def set_operation(self, expression: exp.Expression, op: str) -> str:
    1945        this = self.sql(expression, "this")
    1946        op = self.seg(op)
    1947        return self.query_modifiers(
    1948            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    1949        )

def tag_sql(self, expression: sqlglot.expressions.Tag) -> str: View Source

    1951    def tag_sql(self, expression: exp.Tag) -> str:
    1952        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"

def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str: View Source

    1954    def token_sql(self, token_type: TokenType) -> str:
    1955        return self.TOKEN_MAPPING.get(token_type, token_type.name)

def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str: View Source

    1957    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    1958        this = self.sql(expression, "this")
    1959        expressions = self.no_identify(self.expressions, expression)
    1960        expressions = (
    1961            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    1962        )
    1963        return f"{this}{expressions}"

def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str: View Source

    1965    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    1966        this = self.sql(expression, "this")
    1967        expressions = self.expressions(expression, flat=True)
    1968        return f"{this}({expressions})"

def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str: View Source

    1970    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    1971        return self.binary(expression, "=>")

def when_sql(self, expression: sqlglot.expressions.When) -> str: View Source

    1973    def when_sql(self, expression: exp.When) -> str:
    1974        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    1975        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    1976        condition = self.sql(expression, "condition")
    1977        condition = f" AND {condition}" if condition else ""
    1978
    1979        then_expression = expression.args.get("then")
    1980        if isinstance(then_expression, exp.Insert):
    1981            then = f"INSERT {self.sql(then_expression, 'this')}"
    1982            if "expression" in then_expression.args:
    1983                then += f" VALUES {self.sql(then_expression, 'expression')}"
    1984        elif isinstance(then_expression, exp.Update):
    1985            if isinstance(then_expression.args.get("expressions"), exp.Star):
    1986                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    1987            else:
    1988                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    1989        else:
    1990            then = self.sql(then_expression)
    1991        return f"WHEN {matched}{source}{condition} THEN {then}"

def merge_sql(self, expression: sqlglot.expressions.Merge) -> str: View Source

    1993    def merge_sql(self, expression: exp.Merge) -> str:
    1994        this = self.sql(expression, "this")
    1995        using = f"USING {self.sql(expression, 'using')}"
    1996        on = f"ON {self.sql(expression, 'on')}"
    1997        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"

def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str: View Source

    1999    def tochar_sql(self, expression: exp.ToChar) -> str:
    2000        if expression.args.get("format"):
    2001            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2002
    2003        return self.sql(exp.cast(expression.this, "text"))