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

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

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

###### Arguments:

- time_mapping (dict): the dictionary of custom time mappings in which the key represents a python time format and the output the target time format
- time_trie (trie): a trie of the time_mapping keys
- pretty (bool): if set to True the returned string will be formatted. Default: False.
- quote_start (str): specifies which starting character to use to delimit quotes. Default: '.
- quote_end (str): specifies which ending character to use to delimit quotes. Default: '.
- identifier_start (str): specifies which starting character to use to delimit identifiers. Default: ".
- identifier_end (str): specifies which ending character to use to delimit identifiers. Default: ".
- identify (bool): if set to True all identifiers will be delimited by the corresponding character.
- normalize (bool): if set to True all identifiers will lower cased
- string_escape (str): specifies a string escape character. Default: '.
- identifier_escape (str): specifies an identifier escape character. Default: ".
- pad (int): determines padding in a formatted string. Default: 2.
- indent (int): determines the size of indentation in a formatted string. Default: 4.
- unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
- normalize_functions (str): normalize function names, "upper", "lower", or None Default: "upper"
- alias_post_tablesample (bool): if the table alias comes after tablesample Default: False
- unsupported_level (ErrorLevel): determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
- null_ordering (str): Indicates the default null ordering method to use if not explicitly set. Options are "nulls_are_small", "nulls_are_large", "nulls_are_last". Default: "nulls_are_small"
- max_unsupported (int): Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
- leading_comma (bool): if the the comma is leading or trailing in select statements Default: False
- max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
- comments: Whether or not to preserve comments in the output SQL code. Default: True

Generator( time_mapping=None, time_trie=None, pretty=None, quote_start=None, quote_end=None, identifier_start=None, identifier_end=None, identify=False, normalize=False, string_escape=None, identifier_escape=None, pad=2, indent=2, index_offset=0, unnest_column_only=False, alias_post_tablesample=False, normalize_functions='upper', unsupported_level=<ErrorLevel.WARN: 'WARN'>, null_ordering=None, max_unsupported=3, leading_comma=False, max_text_width=80, comments=True) View Source

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

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

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

    300    def unsupported(self, message: str) -> None:
    301        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    302            raise UnsupportedError(message)
    303        self.unsupported_messages.append(message)

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

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

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

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

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

    311    def pad_comment(self, comment: str) -> str:
    312        comment = " " + comment if comment[0].strip() else comment
    313        comment = comment + " " if comment[-1].strip() else comment
    314        return comment

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

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

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

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

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

    345    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    346        original = self.identify
    347        self.identify = False
    348        result = func(*args, **kwargs)
    349        self.identify = original
    350        return result

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

    352    def normalize_func(self, name: str) -> str:
    353        if self.normalize_functions == "upper":
    354            return name.upper()
    355        if self.normalize_functions == "lower":
    356            return name.lower()
    357        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    460    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    461        this = self.sql(expression, "this")
    462        kind_sql = self.sql(expression, "kind")
    463        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

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

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

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

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

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

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

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

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

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

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

    510    def uniquecolumnconstraint_sql(self, _) -> str:
    511        return "UNIQUE"

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

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

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

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

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

    632    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    633        with_ = self.sql(expression, "with")
    634        if with_:
    635            sql = f"{with_}{self.sep()}{sql}"
    636        return sql

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

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

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

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

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

    648    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    649        alias = self.sql(expression, "this")
    650        columns = self.expressions(expression, key="columns", flat=True)
    651        columns = f"({columns})" if columns else ""
    652        return f"{alias}{columns}"

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

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

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

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

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

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

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

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

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

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

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

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

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

    707    def except_sql(self, expression: exp.Except) -> str:
    708        return self.prepend_ctes(
    709            expression,
    710            self.set_operation(expression, self.except_op(expression)),
    711        )

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

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

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

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

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

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

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

    728    def hint_sql(self, expression: exp.Hint) -> str:
    729        if self.sql(expression, "this"):
    730            self.unsupported("Hints are not supported")
    731        return ""

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

    733    def index_sql(self, expression: exp.Index) -> str:
    734        this = self.sql(expression, "this")
    735        table = self.sql(expression, "table")
    736        columns = self.sql(expression, "columns")
    737        return f"{this} ON {table} {columns}"

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

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

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

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

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

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

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

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

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

    768    def root_properties(self, properties: exp.Properties) -> str:
    769        if properties.expressions:
    770            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    771        return ""

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

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

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

    787    def with_properties(self, properties: exp.Properties) -> str:
    788        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

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

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

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

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

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

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

    834    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    835        no = "NO " if expression.args.get("no") else ""
    836        protection = " PROTECTION" if expression.args.get("protection") else ""
    837        return f"{no}FALLBACK{protection}"

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

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

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

    845    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    846        freespace = self.sql(expression, "this")
    847        percent = " PERCENT" if expression.args.get("percent") else ""
    848        return f"FREESPACE={freespace}{percent}"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    964    def intersect_sql(self, expression: exp.Intersect) -> str:
    965        return self.prepend_ctes(
    966            expression,
    967            self.set_operation(expression, self.intersect_op(expression)),
    968        )

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

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

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

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

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

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

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

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

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

    982    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    983        fields = expression.args.get("fields")
    984        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    985        escaped = expression.args.get("escaped")
    986        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    987        items = expression.args.get("collection_items")
    988        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    989        keys = expression.args.get("map_keys")
    990        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    991        lines = expression.args.get("lines")
    992        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    993        null = expression.args.get("null")
    994        null = f" NULL DEFINED AS {null}" if null else ""
    995        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1183    def lock_sql(self, expression: exp.Lock) -> str:
    1184        if self.LOCKING_READS_SUPPORTED:
    1185            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
    1186            return self.seg(f"FOR {lock_type}")
    1187
    1188        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1189        return ""

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

    1191    def literal_sql(self, expression: exp.Literal) -> str:
    1192        text = expression.this or ""
    1193        if expression.is_string:
    1194            text = text.replace(self.quote_end, self._escaped_quote_end)
    1195            if self.pretty:
    1196                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1197            text = f"{self.quote_start}{text}{self.quote_end}"
    1198        return text

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

    1200    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1201        local = " LOCAL" if expression.args.get("local") else ""
    1202        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1203        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1204        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1205        partition = self.sql(expression, "partition")
    1206        partition = f" {partition}" if partition else ""
    1207        input_format = self.sql(expression, "input_format")
    1208        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1209        serde = self.sql(expression, "serde")
    1210        serde = f" SERDE {serde}" if serde else ""
    1211        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1213    def null_sql(self, *_) -> str:
    1214        return "NULL"

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

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

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

    1219    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1220        this = self.sql(expression, "this")
    1221        this = f"{this} " if this else this
    1222        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

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

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

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

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

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

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

    1233    def ordered_sql(self, expression: exp.Ordered) -> str:
    1234        desc = expression.args.get("desc")
    1235        asc = not desc
    1236
    1237        nulls_first = expression.args.get("nulls_first")
    1238        nulls_last = not nulls_first
    1239        nulls_are_large = self.null_ordering == "nulls_are_large"
    1240        nulls_are_small = self.null_ordering == "nulls_are_small"
    1241        nulls_are_last = self.null_ordering == "nulls_are_last"
    1242
    1243        sort_order = " DESC" if desc else ""
    1244        nulls_sort_change = ""
    1245        if nulls_first and (
    1246            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1247        ):
    1248            nulls_sort_change = " NULLS FIRST"
    1249        elif (
    1250            nulls_last
    1251            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1252            and not nulls_are_last
    1253        ):
    1254            nulls_sort_change = " NULLS LAST"
    1255
    1256        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1257            self.unsupported(
    1258                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1259            )
    1260            nulls_sort_change = ""
    1261
    1262        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

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

    1264    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1265        partition = self.partition_by_sql(expression)
    1266        order = self.sql(expression, "order")
    1267        measures = self.sql(expression, "measures")
    1268        measures = self.seg(f"MEASURES {measures}") if measures else ""
    1269        rows = self.sql(expression, "rows")
    1270        rows = self.seg(rows) if rows else ""
    1271        after = self.sql(expression, "after")
    1272        after = self.seg(after) if after else ""
    1273        pattern = self.sql(expression, "pattern")
    1274        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1275        define = self.sql(expression, "define")
    1276        define = self.seg(f"DEFINE {define}") if define else ""
    1277        body = "".join(
    1278            (
    1279                partition,
    1280                order,
    1281                measures,
    1282                rows,
    1283                after,
    1284                pattern,
    1285                define,
    1286            )
    1287        )
    1288        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"

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

    1290    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1291        return csv(
    1292            *sqls,
    1293            *[self.sql(sql) for sql in expression.args.get("joins") or []],
    1294            self.sql(expression, "match"),
    1295            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
    1296            self.sql(expression, "where"),
    1297            self.sql(expression, "group"),
    1298            self.sql(expression, "having"),
    1299            self.sql(expression, "qualify"),
    1300            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
    1301            if expression.args.get("windows")
    1302            else "",
    1303            self.sql(expression, "distribute"),
    1304            self.sql(expression, "sort"),
    1305            self.sql(expression, "cluster"),
    1306            self.sql(expression, "order"),
    1307            self.sql(expression, "limit"),
    1308            self.sql(expression, "offset"),
    1309            self.sql(expression, "lock"),
    1310            self.sql(expression, "sample"),
    1311            sep="",
    1312        )

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

    1314    def select_sql(self, expression: exp.Select) -> str:
    1315        hint = self.sql(expression, "hint")
    1316        distinct = self.sql(expression, "distinct")
    1317        distinct = f" {distinct}" if distinct else ""
    1318        expressions = self.expressions(expression)
    1319        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1320        sql = self.query_modifiers(
    1321            expression,
    1322            f"SELECT{hint}{distinct}{expressions}",
    1323            self.sql(expression, "into", comment=False),
    1324            self.sql(expression, "from", comment=False),
    1325        )
    1326        return self.prepend_ctes(expression, sql)

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

    1328    def schema_sql(self, expression: exp.Schema) -> str:
    1329        this = self.sql(expression, "this")
    1330        this = f"{this} " if this else ""
    1331        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1332        return f"{this}{sql}"

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

    1334    def star_sql(self, expression: exp.Star) -> str:
    1335        except_ = self.expressions(expression, key="except", flat=True)
    1336        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1337        replace = self.expressions(expression, key="replace", flat=True)
    1338        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1339        return f"*{except_}{replace}"

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

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

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

    1344    def parameter_sql(self, expression: exp.Parameter) -> str:
    1345        this = self.sql(expression, "this")
    1346        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1347        return f"{self.PARAMETER_TOKEN}{this}"

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

    1349    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1350        this = self.sql(expression, "this")
    1351        kind = expression.text("kind")
    1352        if kind:
    1353            kind = f"{kind}."
    1354        return f"@@{kind}{this}"

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

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

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

    1359    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1360        alias = self.sql(expression, "alias")
    1361        alias = f"{sep}{alias}" if alias else ""
    1362
    1363        sql = self.query_modifiers(
    1364            expression,
    1365            self.wrap(expression),
    1366            self.expressions(expression, key="pivots", sep=" "),
    1367            alias,
    1368        )
    1369
    1370        return self.prepend_ctes(expression, sql)

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

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

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

    1376    def union_sql(self, expression: exp.Union) -> str:
    1377        return self.prepend_ctes(
    1378            expression,
    1379            self.set_operation(expression, self.union_op(expression)),
    1380        )

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

    1382    def union_op(self, expression: exp.Union) -> str:
    1383        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1384        kind = kind if expression.args.get("distinct") else " ALL"
    1385        return f"UNION{kind}"

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

    1387    def unnest_sql(self, expression: exp.Unnest) -> str:
    1388        args = self.expressions(expression, flat=True)
    1389        alias = expression.args.get("alias")
    1390        if alias and self.unnest_column_only:
    1391            columns = alias.columns
    1392            alias = self.sql(columns[0]) if columns else ""
    1393        else:
    1394            alias = self.sql(expression, "alias")
    1395        alias = f" AS {alias}" if alias else alias
    1396        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1397        offset = expression.args.get("offset")
    1398        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1399        return f"UNNEST({args}){ordinality}{alias}{offset}"

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

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

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

    1405    def window_sql(self, expression: exp.Window) -> str:
    1406        this = self.sql(expression, "this")
    1407
    1408        partition = self.partition_by_sql(expression)
    1409
    1410        order = expression.args.get("order")
    1411        order_sql = self.order_sql(order, flat=True) if order else ""
    1412
    1413        partition_sql = partition + " " if partition and order else partition
    1414
    1415        spec = expression.args.get("spec")
    1416        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
    1417
    1418        alias = self.sql(expression, "alias")
    1419        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
    1420
    1421        if not partition and not order and not spec and alias:
    1422            return f"{this} {alias}"
    1423
    1424        window_args = alias + partition_sql + order_sql + spec_sql
    1425
    1426        return f"{this} ({window_args.strip()})"

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

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

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

    1432    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
    1433        kind = self.sql(expression, "kind")
    1434        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1435        end = (
    1436            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1437            or "CURRENT ROW"
    1438        )
    1439        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    1446    def between_sql(self, expression: exp.Between) -> str:
    1447        this = self.sql(expression, "this")
    1448        low = self.sql(expression, "low")
    1449        high = self.sql(expression, "high")
    1450        return f"{this} BETWEEN {low} AND {high}"

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

    1452    def bracket_sql(self, expression: exp.Bracket) -> str:
    1453        expressions = apply_index_offset(expression.expressions, self.index_offset)
    1454        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1455
    1456        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    1461    def any_sql(self, expression: exp.Any) -> str:
    1462        this = self.sql(expression, "this")
    1463        if isinstance(expression.this, exp.Subqueryable):
    1464            this = self.wrap(this)
    1465        return f"ANY {this}"

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

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

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

    1470    def case_sql(self, expression: exp.Case) -> str:
    1471        this = self.sql(expression, "this")
    1472        statements = [f"CASE {this}" if this else "CASE"]
    1473
    1474        for e in expression.args["ifs"]:
    1475            statements.append(f"WHEN {self.sql(e, 'this')}")
    1476            statements.append(f"THEN {self.sql(e, 'true')}")
    1477
    1478        default = self.sql(expression, "default")
    1479
    1480        if default:
    1481            statements.append(f"ELSE {default}")
    1482
    1483        statements.append("END")
    1484
    1485        if self.pretty and self.text_width(statements) > self._max_text_width:
    1486            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1487
    1488        return " ".join(statements)

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

    1490    def constraint_sql(self, expression: exp.Constraint) -> str:
    1491        this = self.sql(expression, "this")
    1492        expressions = self.expressions(expression, flat=True)
    1493        return f"CONSTRAINT {this} {expressions}"

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

    1495    def extract_sql(self, expression: exp.Extract) -> str:
    1496        this = self.sql(expression, "this")
    1497        expression_sql = self.sql(expression, "expression")
    1498        return f"EXTRACT({this} FROM {expression_sql})"

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

    1500    def trim_sql(self, expression: exp.Trim) -> str:
    1501        trim_type = self.sql(expression, "position")
    1502
    1503        if trim_type == "LEADING":
    1504            return self.func("LTRIM", expression.this)
    1505        elif trim_type == "TRAILING":
    1506            return self.func("RTRIM", expression.this)
    1507        else:
    1508            return self.func("TRIM", expression.this, expression.expression)

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

    1510    def concat_sql(self, expression: exp.Concat) -> str:
    1511        if len(expression.expressions) == 1:
    1512            return self.sql(expression.expressions[0])
    1513        return self.function_fallback_sql(expression)

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

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

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

    1519    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1520        expressions = self.expressions(expression, flat=True)
    1521        reference = self.sql(expression, "reference")
    1522        reference = f" {reference}" if reference else ""
    1523        delete = self.sql(expression, "delete")
    1524        delete = f" ON DELETE {delete}" if delete else ""
    1525        update = self.sql(expression, "update")
    1526        update = f" ON UPDATE {update}" if update else ""
    1527        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    1529    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1530        expressions = self.expressions(expression, flat=True)
    1531        options = self.expressions(expression, "options", flat=True, sep=" ")
    1532        options = f" {options}" if options else ""
    1533        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

    1539    def if_sql(self, expression: exp.If) -> str:
    1540        return self.case_sql(
    1541            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1542        )

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

    1544    def in_sql(self, expression: exp.In) -> str:
    1545        query = expression.args.get("query")
    1546        unnest = expression.args.get("unnest")
    1547        field = expression.args.get("field")
    1548        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1549
    1550        if query:
    1551            in_sql = self.wrap(query)
    1552        elif unnest:
    1553            in_sql = self.in_unnest_op(unnest)
    1554        elif field:
    1555            in_sql = self.sql(field)
    1556        else:
    1557            in_sql = f"({self.expressions(expression, flat=True)})"
    1558
    1559        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    1564    def interval_sql(self, expression: exp.Interval) -> str:
    1565        this = expression.args.get("this")
    1566        if this:
    1567            this = (
    1568                f" {this}"
    1569                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
    1570                else f" ({this})"
    1571            )
    1572        else:
    1573            this = ""
    1574        unit = self.sql(expression, "unit")
    1575        unit = f" {unit}" if unit else ""
    1576        return f"INTERVAL{this}{unit}"

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

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

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

    1581    def reference_sql(self, expression: exp.Reference) -> str:
    1582        this = self.sql(expression, "this")
    1583        expressions = self.expressions(expression, flat=True)
    1584        expressions = f"({expressions})" if expressions else ""
    1585        options = self.expressions(expression, "options", flat=True, sep=" ")
    1586        options = f" {options}" if options else ""
    1587        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    1592    def paren_sql(self, expression: exp.Paren) -> str:
    1593        if isinstance(expression.unnest(), exp.Select):
    1594            sql = self.wrap(expression)
    1595        else:
    1596            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1597            sql = f"({sql}{self.seg(')', sep='')}"
    1598
    1599        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    1610    def alias_sql(self, expression: exp.Alias) -> str:
    1611        to_sql = self.sql(expression, "alias")
    1612        to_sql = f" AS {to_sql}" if to_sql else ""
    1613        return f"{self.sql(expression, 'this')}{to_sql}"

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

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

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

    1618    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1619        this = self.sql(expression, "this")
    1620        zone = self.sql(expression, "zone")
    1621        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

    1629    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1630        if not self.pretty:
    1631            return self.binary(expression, op)
    1632
    1633        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
    1634        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1635        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1668    def comment_sql(self, expression: exp.Comment) -> str:
    1669        this = self.sql(expression, "this")
    1670        kind = expression.args["kind"]
    1671        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1672        expression_sql = self.sql(expression, "expression")
    1673        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    1675    def transaction_sql(self, *_) -> str:
    1676        return "BEGIN"

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

    1678    def commit_sql(self, expression: exp.Commit) -> str:
    1679        chain = expression.args.get("chain")
    1680        if chain is not None:
    1681            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    1682
    1683        return f"COMMIT{chain or ''}"

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

    1685    def rollback_sql(self, expression: exp.Rollback) -> str:
    1686        savepoint = expression.args.get("savepoint")
    1687        savepoint = f" TO {savepoint}" if savepoint else ""
    1688        return f"ROLLBACK{savepoint}"

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

    1690    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    1691        this = self.sql(expression, "this")
    1692
    1693        dtype = self.sql(expression, "dtype")
    1694        if dtype:
    1695            collate = self.sql(expression, "collate")
    1696            collate = f" COLLATE {collate}" if collate else ""
    1697            using = self.sql(expression, "using")
    1698            using = f" USING {using}" if using else ""
    1699            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    1700
    1701        default = self.sql(expression, "default")
    1702        if default:
    1703            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    1704
    1705        if not expression.args.get("drop"):
    1706            self.unsupported("Unsupported ALTER COLUMN syntax")
    1707
    1708        return f"ALTER COLUMN {this} DROP DEFAULT"

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

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

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

    1714    def altertable_sql(self, expression: exp.AlterTable) -> str:
    1715        actions = expression.args["actions"]
    1716
    1717        if isinstance(actions[0], exp.ColumnDef):
    1718            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
    1719        elif isinstance(actions[0], exp.Schema):
    1720            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
    1721        elif isinstance(actions[0], exp.Delete):
    1722            actions = self.expressions(expression, "actions", flat=True)
    1723        else:
    1724            actions = self.expressions(expression, "actions")
    1725
    1726        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1727        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"

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

    1729    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    1730        expressions = self.expressions(expression)
    1731        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1732        return f"DROP{exists}{expressions}"

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

    1734    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    1735        this = self.sql(expression, "this")
    1736        expression_ = self.sql(expression, "expression")
    1737        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    1738
    1739        enforced = expression.args.get("enforced")
    1740        if enforced is not None:
    1741            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    1742
    1743        return f"{add_constraint} {expression_}"

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

    1745    def distinct_sql(self, expression: exp.Distinct) -> str:
    1746        this = self.expressions(expression, flat=True)
    1747        this = f" {this}" if this else ""
    1748
    1749        on = self.sql(expression, "on")
    1750        on = f" ON {on}" if on else ""
    1751        return f"DISTINCT{this}{on}"

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

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

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

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

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

    1759    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    1760        div = self.binary(expression, "/")
    1761        return self.sql(exp.Cast(this=div, to=exp.DataType.build("INT")))

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

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

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

    1766    def div_sql(self, expression: exp.Div) -> str:
    1767        div = self.binary(expression, "/")
    1768
    1769        if not self.INTEGER_DIVISION:
    1770            return self.sql(exp.Cast(this=div, to=exp.DataType.build("INT")))
    1771
    1772        return div

def floatdiv_sql(self, expression: sqlglot.expressions.FloatDiv) -> str: View Source

    1774    def floatdiv_sql(self, expression: exp.FloatDiv) -> str:
    1775        if self.INTEGER_DIVISION:
    1776            this = exp.Cast(this=expression.this, to=exp.DataType.build("DOUBLE"))
    1777            return self.div_sql(exp.Div(this=this, expression=expression.expression))
    1778
    1779        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1850    def use_sql(self, expression: exp.Use) -> str:
    1851        kind = self.sql(expression, "kind")
    1852        kind = f" {kind}" if kind else ""
    1853        this = self.sql(expression, "this")
    1854        this = f" {this}" if this else ""
    1855        return f"USE{kind}{this}"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1940    def set_operation(self, expression: exp.Expression, op: str) -> str:
    1941        this = self.sql(expression, "this")
    1942        op = self.seg(op)
    1943        return self.query_modifiers(
    1944            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    1945        )

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

    1947    def tag_sql(self, expression: exp.Tag) -> str:
    1948        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

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

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

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

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

    1961    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    1962        this = self.sql(expression, "this")
    1963        expressions = self.expressions(expression, flat=True)
    1964        return f"{this}({expressions})"

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

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

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

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

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

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