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

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

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

###### Arguments:

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

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

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

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

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

Generates a SQL string by interpreting the given syntax tree.

Args expression: the syntax tree.

Returns the SQL string.

def unsupported(self, message: str) -> None: View Source

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    781    def with_properties(self, properties: exp.Properties) -> str:
    782        return self.properties(properties, prefix=self.seg("WITH"))

def locate_properties( self, properties: sqlglot.expressions.Properties) -> Dict[sqlglot.expressions.Properties.Location, list[sqlglot.expressions.Property]]: View Source

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    957    def intersect_sql(self, expression: exp.Intersect) -> str:
    958        return self.prepend_ctes(
    959            expression,
    960            self.set_operation(expression, self.intersect_op(expression)),
    961        )

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

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

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

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

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

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

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

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

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

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

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

    1010    def tablesample_sql(self, expression: exp.TableSample) -> str:
    1011        if self.alias_post_tablesample and expression.this.alias:
    1012            this = self.sql(expression.this, "this")
    1013            alias = f" AS {self.sql(expression.this, 'alias')}"
    1014        else:
    1015            this = self.sql(expression, "this")
    1016            alias = ""
    1017        method = self.sql(expression, "method")
    1018        method = f" {method.upper()} " if method else ""
    1019        numerator = self.sql(expression, "bucket_numerator")
    1020        denominator = self.sql(expression, "bucket_denominator")
    1021        field = self.sql(expression, "bucket_field")
    1022        field = f" ON {field}" if field else ""
    1023        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1024        percent = self.sql(expression, "percent")
    1025        percent = f"{percent} PERCENT" if percent else ""
    1026        rows = self.sql(expression, "rows")
    1027        rows = f"{rows} ROWS" if rows else ""
    1028        size = self.sql(expression, "size")
    1029        seed = self.sql(expression, "seed")
    1030        seed = f" SEED ({seed})" if seed else ""
    1031        return f"{this} TABLESAMPLE{method}({bucket}{percent}{rows}{size}){seed}{alias}"

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

    1033    def pivot_sql(self, expression: exp.Pivot) -> str:
    1034        this = self.sql(expression, "this")
    1035        alias = self.sql(expression, "alias")
    1036        alias = f" AS {alias}" if alias else ""
    1037        unpivot = expression.args.get("unpivot")
    1038        direction = "UNPIVOT" if unpivot else "PIVOT"
    1039        expressions = self.expressions(expression, key="expressions")
    1040        field = self.sql(expression, "field")
    1041        return f"{this} {direction}({expressions} FOR {field}){alias}"

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

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

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

    1046    def update_sql(self, expression: exp.Update) -> str:
    1047        this = self.sql(expression, "this")
    1048        set_sql = self.expressions(expression, flat=True)
    1049        from_sql = self.sql(expression, "from")
    1050        where_sql = self.sql(expression, "where")
    1051        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}"
    1052        return self.prepend_ctes(expression, sql)

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

    1054    def values_sql(self, expression: exp.Values) -> str:
    1055        args = self.expressions(expression)
    1056        alias = self.sql(expression, "alias")
    1057        values = f"VALUES{self.seg('')}{args}"
    1058        values = (
    1059            f"({values})"
    1060            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1061            else values
    1062        )
    1063        return f"{values} AS {alias}" if alias else values

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1145    def lateral_sql(self, expression: exp.Lateral) -> str:
    1146        this = self.sql(expression, "this")
    1147
    1148        if isinstance(expression.this, exp.Subquery):
    1149            return f"LATERAL {this}"
    1150
    1151        if expression.args.get("view"):
    1152            alias = expression.args["alias"]
    1153            columns = self.expressions(alias, key="columns", flat=True)
    1154            table = f" {alias.name}" if alias.name else ""
    1155            columns = f" AS {columns}" if columns else ""
    1156            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1157            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1158
    1159        alias = self.sql(expression, "alias")
    1160        alias = f" AS {alias}" if alias else ""
    1161        return f"LATERAL {this}{alias}"

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

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

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

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

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

    1171    def lock_sql(self, expression: exp.Lock) -> str:
    1172        if self.LOCKING_READS_SUPPORTED:
    1173            lock_type = "UPDATE" if expression.args["update"] else "SHARE"
    1174            return self.seg(f"FOR {lock_type}")
    1175
    1176        self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1177        return ""

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

    1179    def literal_sql(self, expression: exp.Literal) -> str:
    1180        text = expression.this or ""
    1181        if expression.is_string:
    1182            text = text.replace(self.quote_end, self._escaped_quote_end)
    1183            if self.pretty:
    1184                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1185            text = f"{self.quote_start}{text}{self.quote_end}"
    1186        return text

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

    1188    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1189        local = " LOCAL" if expression.args.get("local") else ""
    1190        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1191        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1192        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1193        partition = self.sql(expression, "partition")
    1194        partition = f" {partition}" if partition else ""
    1195        input_format = self.sql(expression, "input_format")
    1196        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1197        serde = self.sql(expression, "serde")
    1198        serde = f" SERDE {serde}" if serde else ""
    1199        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1201    def null_sql(self, *_) -> str:
    1202        return "NULL"

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

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

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

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

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

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

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

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

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

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

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

    1221    def ordered_sql(self, expression: exp.Ordered) -> str:
    1222        desc = expression.args.get("desc")
    1223        asc = not desc
    1224
    1225        nulls_first = expression.args.get("nulls_first")
    1226        nulls_last = not nulls_first
    1227        nulls_are_large = self.null_ordering == "nulls_are_large"
    1228        nulls_are_small = self.null_ordering == "nulls_are_small"
    1229        nulls_are_last = self.null_ordering == "nulls_are_last"
    1230
    1231        sort_order = " DESC" if desc else ""
    1232        nulls_sort_change = ""
    1233        if nulls_first and (
    1234            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1235        ):
    1236            nulls_sort_change = " NULLS FIRST"
    1237        elif (
    1238            nulls_last
    1239            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1240            and not nulls_are_last
    1241        ):
    1242            nulls_sort_change = " NULLS LAST"
    1243
    1244        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1245            self.unsupported(
    1246                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1247            )
    1248            nulls_sort_change = ""
    1249
    1250        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

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

    1252    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1253        partition = self.partition_by_sql(expression)
    1254        order = self.sql(expression, "order")
    1255        measures = self.sql(expression, "measures")
    1256        measures = self.seg(f"MEASURES {measures}") if measures else ""
    1257        rows = self.sql(expression, "rows")
    1258        rows = self.seg(rows) if rows else ""
    1259        after = self.sql(expression, "after")
    1260        after = self.seg(after) if after else ""
    1261        pattern = self.sql(expression, "pattern")
    1262        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1263        define = self.sql(expression, "define")
    1264        define = self.seg(f"DEFINE {define}") if define else ""
    1265        body = "".join(
    1266            (
    1267                partition,
    1268                order,
    1269                measures,
    1270                rows,
    1271                after,
    1272                pattern,
    1273                define,
    1274            )
    1275        )
    1276        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}"

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

    1278    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1279        return csv(
    1280            *sqls,
    1281            *[self.sql(sql) for sql in expression.args.get("joins") or []],
    1282            self.sql(expression, "match"),
    1283            *[self.sql(sql) for sql in expression.args.get("laterals") or []],
    1284            self.sql(expression, "where"),
    1285            self.sql(expression, "group"),
    1286            self.sql(expression, "having"),
    1287            self.sql(expression, "qualify"),
    1288            self.seg("WINDOW ") + self.expressions(expression, "windows", flat=True)
    1289            if expression.args.get("windows")
    1290            else "",
    1291            self.sql(expression, "distribute"),
    1292            self.sql(expression, "sort"),
    1293            self.sql(expression, "cluster"),
    1294            self.sql(expression, "order"),
    1295            self.sql(expression, "limit"),
    1296            self.sql(expression, "offset"),
    1297            self.sql(expression, "lock"),
    1298            sep="",
    1299        )

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

    1301    def select_sql(self, expression: exp.Select) -> str:
    1302        hint = self.sql(expression, "hint")
    1303        distinct = self.sql(expression, "distinct")
    1304        distinct = f" {distinct}" if distinct else ""
    1305        expressions = self.expressions(expression)
    1306        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1307        sql = self.query_modifiers(
    1308            expression,
    1309            f"SELECT{hint}{distinct}{expressions}",
    1310            self.sql(expression, "into", comment=False),
    1311            self.sql(expression, "from", comment=False),
    1312        )
    1313        return self.prepend_ctes(expression, sql)

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

    1315    def schema_sql(self, expression: exp.Schema) -> str:
    1316        this = self.sql(expression, "this")
    1317        this = f"{this} " if this else ""
    1318        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1319        return f"{this}{sql}"

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

    1321    def star_sql(self, expression: exp.Star) -> str:
    1322        except_ = self.expressions(expression, key="except", flat=True)
    1323        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1324        replace = self.expressions(expression, key="replace", flat=True)
    1325        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1326        return f"*{except_}{replace}"

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

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

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

    1331    def parameter_sql(self, expression: exp.Parameter) -> str:
    1332        this = self.sql(expression, "this")
    1333        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1334        return f"{self.PARAMETER_TOKEN}{this}"

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

    1336    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1337        this = self.sql(expression, "this")
    1338        kind = expression.text("kind")
    1339        if kind:
    1340            kind = f"{kind}."
    1341        return f"@@{kind}{this}"

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

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

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

    1346    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1347        alias = self.sql(expression, "alias")
    1348        alias = f"{sep}{alias}" if alias else ""
    1349
    1350        sql = self.query_modifiers(
    1351            expression,
    1352            self.wrap(expression),
    1353            self.expressions(expression, key="pivots", sep=" "),
    1354            alias,
    1355        )
    1356
    1357        return self.prepend_ctes(expression, sql)

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

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

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

    1363    def union_sql(self, expression: exp.Union) -> str:
    1364        return self.prepend_ctes(
    1365            expression,
    1366            self.set_operation(expression, self.union_op(expression)),
    1367        )

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

    1369    def union_op(self, expression: exp.Union) -> str:
    1370        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1371        kind = kind if expression.args.get("distinct") else " ALL"
    1372        return f"UNION{kind}"

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

    1374    def unnest_sql(self, expression: exp.Unnest) -> str:
    1375        args = self.expressions(expression, flat=True)
    1376        alias = expression.args.get("alias")
    1377        if alias and self.unnest_column_only:
    1378            columns = alias.columns
    1379            alias = self.sql(columns[0]) if columns else ""
    1380        else:
    1381            alias = self.sql(expression, "alias")
    1382        alias = f" AS {alias}" if alias else alias
    1383        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1384        offset = expression.args.get("offset")
    1385        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1386        return f"UNNEST({args}){ordinality}{alias}{offset}"

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

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

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

    1392    def window_sql(self, expression: exp.Window) -> str:
    1393        this = self.sql(expression, "this")
    1394
    1395        partition = self.partition_by_sql(expression)
    1396
    1397        order = expression.args.get("order")
    1398        order_sql = self.order_sql(order, flat=True) if order else ""
    1399
    1400        partition_sql = partition + " " if partition and order else partition
    1401
    1402        spec = expression.args.get("spec")
    1403        spec_sql = " " + self.window_spec_sql(spec) if spec else ""
    1404
    1405        alias = self.sql(expression, "alias")
    1406        this = f"{this} {'AS' if expression.arg_key == 'windows' else 'OVER'}"
    1407
    1408        if not partition and not order and not spec and alias:
    1409            return f"{this} {alias}"
    1410
    1411        window_args = alias + partition_sql + order_sql + spec_sql
    1412
    1413        return f"{this} ({window_args.strip()})"

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

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

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

    1419    def window_spec_sql(self, expression: exp.WindowSpec) -> str:
    1420        kind = self.sql(expression, "kind")
    1421        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1422        end = (
    1423            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1424            or "CURRENT ROW"
    1425        )
    1426        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    1433    def between_sql(self, expression: exp.Between) -> str:
    1434        this = self.sql(expression, "this")
    1435        low = self.sql(expression, "low")
    1436        high = self.sql(expression, "high")
    1437        return f"{this} BETWEEN {low} AND {high}"

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

    1439    def bracket_sql(self, expression: exp.Bracket) -> str:
    1440        expressions = apply_index_offset(expression.expressions, self.index_offset)
    1441        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1442
    1443        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    1448    def any_sql(self, expression: exp.Any) -> str:
    1449        this = self.sql(expression, "this")
    1450        if isinstance(expression.this, exp.Subqueryable):
    1451            this = self.wrap(this)
    1452        return f"ANY {this}"

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

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

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

    1457    def case_sql(self, expression: exp.Case) -> str:
    1458        this = self.sql(expression, "this")
    1459        statements = [f"CASE {this}" if this else "CASE"]
    1460
    1461        for e in expression.args["ifs"]:
    1462            statements.append(f"WHEN {self.sql(e, 'this')}")
    1463            statements.append(f"THEN {self.sql(e, 'true')}")
    1464
    1465        default = self.sql(expression, "default")
    1466
    1467        if default:
    1468            statements.append(f"ELSE {default}")
    1469
    1470        statements.append("END")
    1471
    1472        if self.pretty and self.text_width(statements) > self._max_text_width:
    1473            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1474
    1475        return " ".join(statements)

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

    1477    def constraint_sql(self, expression: exp.Constraint) -> str:
    1478        this = self.sql(expression, "this")
    1479        expressions = self.expressions(expression, flat=True)
    1480        return f"CONSTRAINT {this} {expressions}"

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

    1482    def extract_sql(self, expression: exp.Extract) -> str:
    1483        this = self.sql(expression, "this")
    1484        expression_sql = self.sql(expression, "expression")
    1485        return f"EXTRACT({this} FROM {expression_sql})"

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

    1487    def trim_sql(self, expression: exp.Trim) -> str:
    1488        trim_type = self.sql(expression, "position")
    1489
    1490        if trim_type == "LEADING":
    1491            return self.func("LTRIM", expression.this)
    1492        elif trim_type == "TRAILING":
    1493            return self.func("RTRIM", expression.this)
    1494        else:
    1495            return self.func("TRIM", expression.this, expression.expression)

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

    1497    def concat_sql(self, expression: exp.Concat) -> str:
    1498        if len(expression.expressions) == 1:
    1499            return self.sql(expression.expressions[0])
    1500        return self.function_fallback_sql(expression)

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

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

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

    1506    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1507        expressions = self.expressions(expression, flat=True)
    1508        reference = self.sql(expression, "reference")
    1509        reference = f" {reference}" if reference else ""
    1510        delete = self.sql(expression, "delete")
    1511        delete = f" ON DELETE {delete}" if delete else ""
    1512        update = self.sql(expression, "update")
    1513        update = f" ON UPDATE {update}" if update else ""
    1514        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    1516    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1517        expressions = self.expressions(expression, flat=True)
    1518        options = self.expressions(expression, "options", flat=True, sep=" ")
    1519        options = f" {options}" if options else ""
    1520        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

    1526    def if_sql(self, expression: exp.If) -> str:
    1527        return self.case_sql(
    1528            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1529        )

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

    1531    def in_sql(self, expression: exp.In) -> str:
    1532        query = expression.args.get("query")
    1533        unnest = expression.args.get("unnest")
    1534        field = expression.args.get("field")
    1535        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1536
    1537        if query:
    1538            in_sql = self.wrap(query)
    1539        elif unnest:
    1540            in_sql = self.in_unnest_op(unnest)
    1541        elif field:
    1542            in_sql = self.sql(field)
    1543        else:
    1544            in_sql = f"({self.expressions(expression, flat=True)})"
    1545
    1546        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    1551    def interval_sql(self, expression: exp.Interval) -> str:
    1552        this = expression.args.get("this")
    1553        if this:
    1554            this = (
    1555                f" {this}"
    1556                if isinstance(this, exp.Literal) or isinstance(this, exp.Paren)
    1557                else f" ({this})"
    1558            )
    1559        else:
    1560            this = ""
    1561        unit = expression.args.get("unit")
    1562        unit = f" {unit}" if unit else ""
    1563        return f"INTERVAL{this}{unit}"

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

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

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

    1568    def reference_sql(self, expression: exp.Reference) -> str:
    1569        this = self.sql(expression, "this")
    1570        expressions = self.expressions(expression, flat=True)
    1571        expressions = f"({expressions})" if expressions else ""
    1572        options = self.expressions(expression, "options", flat=True, sep=" ")
    1573        options = f" {options}" if options else ""
    1574        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    1579    def paren_sql(self, expression: exp.Paren) -> str:
    1580        if isinstance(expression.unnest(), exp.Select):
    1581            sql = self.wrap(expression)
    1582        else:
    1583            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1584            sql = f"({sql}{self.seg(')', sep='')}"
    1585
    1586        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    1597    def alias_sql(self, expression: exp.Alias) -> str:
    1598        to_sql = self.sql(expression, "alias")
    1599        to_sql = f" AS {to_sql}" if to_sql else ""
    1600        return f"{self.sql(expression, 'this')}{to_sql}"

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

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

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

    1605    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1606        this = self.sql(expression, "this")
    1607        zone = self.sql(expression, "zone")
    1608        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

    1616    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1617        if not self.pretty:
    1618            return self.binary(expression, op)
    1619
    1620        sqls = tuple(self.sql(e) for e in expression.flatten(unnest=False))
    1621        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1622        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1655    def comment_sql(self, expression: exp.Comment) -> str:
    1656        this = self.sql(expression, "this")
    1657        kind = expression.args["kind"]
    1658        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1659        expression_sql = self.sql(expression, "expression")
    1660        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    1662    def transaction_sql(self, *_) -> str:
    1663        return "BEGIN"

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

    1665    def commit_sql(self, expression: exp.Commit) -> str:
    1666        chain = expression.args.get("chain")
    1667        if chain is not None:
    1668            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    1669
    1670        return f"COMMIT{chain or ''}"

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

    1672    def rollback_sql(self, expression: exp.Rollback) -> str:
    1673        savepoint = expression.args.get("savepoint")
    1674        savepoint = f" TO {savepoint}" if savepoint else ""
    1675        return f"ROLLBACK{savepoint}"

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

    1677    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    1678        this = self.sql(expression, "this")
    1679
    1680        dtype = self.sql(expression, "dtype")
    1681        if dtype:
    1682            collate = self.sql(expression, "collate")
    1683            collate = f" COLLATE {collate}" if collate else ""
    1684            using = self.sql(expression, "using")
    1685            using = f" USING {using}" if using else ""
    1686            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    1687
    1688        default = self.sql(expression, "default")
    1689        if default:
    1690            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    1691
    1692        if not expression.args.get("drop"):
    1693            self.unsupported("Unsupported ALTER COLUMN syntax")
    1694
    1695        return f"ALTER COLUMN {this} DROP DEFAULT"

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

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

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

    1701    def altertable_sql(self, expression: exp.AlterTable) -> str:
    1702        actions = expression.args["actions"]
    1703
    1704        if isinstance(actions[0], exp.ColumnDef):
    1705            actions = self.expressions(expression, "actions", prefix="ADD COLUMN ")
    1706        elif isinstance(actions[0], exp.Schema):
    1707            actions = self.expressions(expression, "actions", prefix="ADD COLUMNS ")
    1708        elif isinstance(actions[0], exp.Delete):
    1709            actions = self.expressions(expression, "actions", flat=True)
    1710        else:
    1711            actions = self.expressions(expression, "actions")
    1712
    1713        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1714        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"

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

    1716    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    1717        expressions = self.expressions(expression)
    1718        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1719        return f"DROP{exists}{expressions}"

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

    1721    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    1722        this = self.sql(expression, "this")
    1723        expression_ = self.sql(expression, "expression")
    1724        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    1725
    1726        enforced = expression.args.get("enforced")
    1727        if enforced is not None:
    1728            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    1729
    1730        return f"{add_constraint} {expression_}"

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

    1732    def distinct_sql(self, expression: exp.Distinct) -> str:
    1733        this = self.expressions(expression, flat=True)
    1734        this = f" {this}" if this else ""
    1735
    1736        on = self.sql(expression, "on")
    1737        on = f" ON {on}" if on else ""
    1738        return f"DISTINCT{this}{on}"

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

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

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

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

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

    1746    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    1747        return self.sql(
    1748            exp.Cast(
    1749                this=exp.Div(this=expression.this, expression=expression.expression),
    1750                to=exp.DataType(this=exp.DataType.Type.INT),
    1751            )
    1752        )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1829    def use_sql(self, expression: exp.Use) -> str:
    1830        kind = self.sql(expression, "kind")
    1831        kind = f" {kind}" if kind else ""
    1832        this = self.sql(expression, "this")
    1833        this = f" {this}" if this else ""
    1834        return f"USE{kind}{this}"

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

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

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

    1839    def function_fallback_sql(self, expression: exp.Func) -> str:
    1840        args = []
    1841        for arg_value in expression.args.values():
    1842            if isinstance(arg_value, list):
    1843                for value in arg_value:
    1844                    args.append(value)
    1845            else:
    1846                args.append(arg_value)
    1847
    1848        return self.func(expression.sql_name(), *args)

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

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

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

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

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

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

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

    1862    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    1863        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)

def expressions( self, expression: sqlglot.expressions.Expression, key: Optional[str] = None, flat: bool = False, indent: bool = True, sep: str = ', ', prefix: str = '') -> str: View Source

    1865    def expressions(
    1866        self,
    1867        expression: exp.Expression,
    1868        key: t.Optional[str] = None,
    1869        flat: bool = False,
    1870        indent: bool = True,
    1871        sep: str = ", ",
    1872        prefix: str = "",
    1873    ) -> str:
    1874        expressions = expression.args.get(key or "expressions")
    1875
    1876        if not expressions:
    1877            return ""
    1878
    1879        if flat:
    1880            return sep.join(self.sql(e) for e in expressions)
    1881
    1882        num_sqls = len(expressions)
    1883
    1884        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    1885        pad = " " * self.pad
    1886        stripped_sep = sep.strip()
    1887
    1888        result_sqls = []
    1889        for i, e in enumerate(expressions):
    1890            sql = self.sql(e, comment=False)
    1891            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    1892
    1893            if self.pretty:
    1894                if self._leading_comma:
    1895                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    1896                else:
    1897                    result_sqls.append(
    1898                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    1899                    )
    1900            else:
    1901                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    1902
    1903        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    1904        return self.indent(result_sql, skip_first=False) if indent else result_sql

def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str: View Source

    1906    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    1907        flat = flat or isinstance(expression.parent, exp.Properties)
    1908        expressions_sql = self.expressions(expression, flat=flat)
    1909        if flat:
    1910            return f"{op} {expressions_sql}"
    1911        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    1913    def naked_property(self, expression: exp.Property) -> str:
    1914        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    1915        if not property_name:
    1916            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    1917        return f"{property_name} {self.sql(expression, 'this')}"

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

    1919    def set_operation(self, expression: exp.Expression, op: str) -> str:
    1920        this = self.sql(expression, "this")
    1921        op = self.seg(op)
    1922        return self.query_modifiers(
    1923            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    1924        )

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

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

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

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

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

    1932    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    1933        this = self.sql(expression, "this")
    1934        expressions = self.no_identify(self.expressions, expression)
    1935        expressions = (
    1936            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    1937        )
    1938        return f"{this}{expressions}"

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

    1940    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    1941        this = self.sql(expression, "this")
    1942        expressions = self.expressions(expression, flat=True)
    1943        return f"{this}({expressions})"

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

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

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

    1948    def when_sql(self, expression: exp.When) -> str:
    1949        this = self.sql(expression, "this")
    1950        then_expression = expression.args.get("then")
    1951        if isinstance(then_expression, exp.Insert):
    1952            then = f"INSERT {self.sql(then_expression, 'this')}"
    1953            if "expression" in then_expression.args:
    1954                then += f" VALUES {self.sql(then_expression, 'expression')}"
    1955        elif isinstance(then_expression, exp.Update):
    1956            if isinstance(then_expression.args.get("expressions"), exp.Star):
    1957                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    1958            else:
    1959                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    1960        else:
    1961            then = self.sql(then_expression)
    1962        return f"WHEN {this} THEN {then}"

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

    1964    def merge_sql(self, expression: exp.Merge) -> str:
    1965        this = self.sql(expression, "this")
    1966        using = f"USING {self.sql(expression, 'using')}"
    1967        on = f"ON {self.sql(expression, 'on')}"
    1968        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"