sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

       1from __future__ import annotations
       2
       3import logging
       4import typing as t
       5from collections import defaultdict
       6from functools import reduce
       7
       8from sqlglot import exp
       9from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
      10from sqlglot.helper import apply_index_offset, csv, seq_get
      11from sqlglot.time import format_time
      12from sqlglot.tokens import TokenType
      13
      14if t.TYPE_CHECKING:
      15    from sqlglot._typing import E
      16    from sqlglot.dialects.dialect import DialectType
      17
      18logger = logging.getLogger("sqlglot")
      19
      20
      21class Generator:
      22    """
      23    Generator converts a given syntax tree to the corresponding SQL string.
      24
      25    Args:
      26        pretty: Whether or not to format the produced SQL string.
      27            Default: False.
      28        identify: Determines when an identifier should be quoted. Possible values are:
      29            False (default): Never quote, except in cases where it's mandatory by the dialect.
      30            True or 'always': Always quote.
      31            'safe': Only quote identifiers that are case insensitive.
      32        normalize: Whether or not to normalize identifiers to lowercase.
      33            Default: False.
      34        pad: Determines the pad size in a formatted string.
      35            Default: 2.
      36        indent: Determines the indentation size in a formatted string.
      37            Default: 2.
      38        normalize_functions: Whether or not to normalize all function names. Possible values are:
      39            "upper" or True (default): Convert names to uppercase.
      40            "lower": Convert names to lowercase.
      41            False: Disables function name normalization.
      42        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
      43            Default ErrorLevel.WARN.
      44        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
      45            This is only relevant if unsupported_level is ErrorLevel.RAISE.
      46            Default: 3
      47        leading_comma: Determines whether or not the comma is leading or trailing in select expressions.
      48            This is only relevant when generating in pretty mode.
      49            Default: False
      50        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
      51            The default is on the smaller end because the length only represents a segment and not the true
      52            line length.
      53            Default: 80
      54        comments: Whether or not to preserve comments in the output SQL code.
      55            Default: True
      56    """
      57
      58    TRANSFORMS = {
      59        exp.DateAdd: lambda self, e: self.func(
      60            "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      61        ),
      62        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
      63        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
      64        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
      65        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
      66        exp.ClusteredColumnConstraint: lambda self, e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
      67        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
      68        exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS",
      69        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
      70        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
      71        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
      72        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
      73        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
      74        exp.ExternalProperty: lambda self, e: "EXTERNAL",
      75        exp.HeapProperty: lambda self, e: "HEAP",
      76        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
      77        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
      78        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
      79        exp.LanguageProperty: lambda self, e: self.naked_property(e),
      80        exp.LocationProperty: lambda self, e: self.naked_property(e),
      81        exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
      82        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
      83        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
      84        exp.NonClusteredColumnConstraint: lambda self, e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
      85        exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION",
      86        exp.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
      87        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
      88        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
      89        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
      90        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
      91        exp.RemoteWithConnectionModelProperty: lambda self, e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
      92        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
      93        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
      94        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
      95        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
      96        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
      97        exp.StabilityProperty: lambda self, e: e.name,
      98        exp.TemporaryProperty: lambda self, e: f"TEMPORARY",
      99        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
     100        exp.TransientProperty: lambda self, e: "TRANSIENT",
     101        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
     102        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     103        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
     104        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
     105        exp.VolatileProperty: lambda self, e: "VOLATILE",
     106        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
     107    }
     108
     109    # Whether or not null ordering is supported in order by
     110    NULL_ORDERING_SUPPORTED = True
     111
     112    # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     113    LOCKING_READS_SUPPORTED = False
     114
     115    # Always do union distinct or union all
     116    EXPLICIT_UNION = False
     117
     118    # Wrap derived values in parens, usually standard but spark doesn't support it
     119    WRAP_DERIVED_VALUES = True
     120
     121    # Whether or not create function uses an AS before the RETURN
     122    CREATE_FUNCTION_RETURN_AS = True
     123
     124    # Whether or not MERGE ... WHEN MATCHED BY SOURCE is allowed
     125    MATCHED_BY_SOURCE = True
     126
     127    # Whether or not the INTERVAL expression works only with values like '1 day'
     128    SINGLE_STRING_INTERVAL = False
     129
     130    # Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     131    INTERVAL_ALLOWS_PLURAL_FORM = True
     132
     133    # Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI
     134    TABLESAMPLE_WITH_METHOD = True
     135
     136    # Whether or not to treat the number in TABLESAMPLE (50) as a percentage
     137    TABLESAMPLE_SIZE_IS_PERCENT = False
     138
     139    # Whether or not limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     140    LIMIT_FETCH = "ALL"
     141
     142    # Whether or not limit and fetch allows expresions or just limits
     143    LIMIT_ONLY_LITERALS = False
     144
     145    # Whether or not a table is allowed to be renamed with a db
     146    RENAME_TABLE_WITH_DB = True
     147
     148    # The separator for grouping sets and rollups
     149    GROUPINGS_SEP = ","
     150
     151    # The string used for creating an index on a table
     152    INDEX_ON = "ON"
     153
     154    # Whether or not join hints should be generated
     155    JOIN_HINTS = True
     156
     157    # Whether or not table hints should be generated
     158    TABLE_HINTS = True
     159
     160    # Whether or not query hints should be generated
     161    QUERY_HINTS = True
     162
     163    # What kind of separator to use for query hints
     164    QUERY_HINT_SEP = ", "
     165
     166    # Whether or not comparing against booleans (e.g. x IS TRUE) is supported
     167    IS_BOOL_ALLOWED = True
     168
     169    # Whether or not to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
     170    DUPLICATE_KEY_UPDATE_WITH_SET = True
     171
     172    # Whether or not to generate the limit as TOP <value> instead of LIMIT <value>
     173    LIMIT_IS_TOP = False
     174
     175    # Whether or not to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
     176    RETURNING_END = True
     177
     178    # Whether or not to generate the (+) suffix for columns used in old-style join conditions
     179    COLUMN_JOIN_MARKS_SUPPORTED = False
     180
     181    # Whether or not to generate an unquoted value for EXTRACT's date part argument
     182    EXTRACT_ALLOWS_QUOTES = True
     183
     184    # Whether or not TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
     185    TZ_TO_WITH_TIME_ZONE = False
     186
     187    # Whether or not the NVL2 function is supported
     188    NVL2_SUPPORTED = True
     189
     190    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
     191    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
     192
     193    # Whether or not VALUES statements can be used as derived tables.
     194    # MySQL 5 and Redshift do not allow this, so when False, it will convert
     195    # SELECT * VALUES into SELECT UNION
     196    VALUES_AS_TABLE = True
     197
     198    # Whether or not the word COLUMN is included when adding a column with ALTER TABLE
     199    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
     200
     201    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     202    UNNEST_WITH_ORDINALITY = True
     203
     204    # Whether or not FILTER (WHERE cond) can be used for conditional aggregation
     205    AGGREGATE_FILTER_SUPPORTED = True
     206
     207    # Whether or not JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
     208    SEMI_ANTI_JOIN_WITH_SIDE = True
     209
     210    # Whether or not to include the type of a computed column in the CREATE DDL
     211    COMPUTED_COLUMN_WITH_TYPE = True
     212
     213    # Whether or not CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
     214    SUPPORTS_TABLE_COPY = True
     215
     216    # Whether or not parentheses are required around the table sample's expression
     217    TABLESAMPLE_REQUIRES_PARENS = True
     218
     219    # Whether or not COLLATE is a function instead of a binary operator
     220    COLLATE_IS_FUNC = False
     221
     222    # Whether or not data types support additional specifiers like e.g. CHAR or BYTE (oracle)
     223    DATA_TYPE_SPECIFIERS_ALLOWED = False
     224
     225    # Whether or not conditions require booleans WHERE x = 0 vs WHERE x
     226    ENSURE_BOOLS = False
     227
     228    # Whether or not the "RECURSIVE" keyword is required when defining recursive CTEs
     229    CTE_RECURSIVE_KEYWORD_REQUIRED = True
     230
     231    # Whether or not CONCAT requires >1 arguments
     232    SUPPORTS_SINGLE_ARG_CONCAT = True
     233
     234    TYPE_MAPPING = {
     235        exp.DataType.Type.NCHAR: "CHAR",
     236        exp.DataType.Type.NVARCHAR: "VARCHAR",
     237        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     238        exp.DataType.Type.LONGTEXT: "TEXT",
     239        exp.DataType.Type.TINYTEXT: "TEXT",
     240        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     241        exp.DataType.Type.LONGBLOB: "BLOB",
     242        exp.DataType.Type.TINYBLOB: "BLOB",
     243        exp.DataType.Type.INET: "INET",
     244    }
     245
     246    STAR_MAPPING = {
     247        "except": "EXCEPT",
     248        "replace": "REPLACE",
     249    }
     250
     251    TIME_PART_SINGULARS = {
     252        "microseconds": "microsecond",
     253        "seconds": "second",
     254        "minutes": "minute",
     255        "hours": "hour",
     256        "days": "day",
     257        "weeks": "week",
     258        "months": "month",
     259        "quarters": "quarter",
     260        "years": "year",
     261    }
     262
     263    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     264
     265    STRUCT_DELIMITER = ("<", ">")
     266
     267    PARAMETER_TOKEN = "@"
     268
     269    PROPERTIES_LOCATION = {
     270        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     271        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     272        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     273        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     274        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     275        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     276        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
     277        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     278        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
     279        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     280        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     281        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     282        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     283        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     284        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     285        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     286        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     287        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     288        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     289        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     290        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     291        exp.HeapProperty: exp.Properties.Location.POST_WITH,
     292        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
     293        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     294        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     295        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     296        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     297        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     298        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     299        exp.LogProperty: exp.Properties.Location.POST_NAME,
     300        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     301        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     302        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     303        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
     304        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     305        exp.Order: exp.Properties.Location.POST_SCHEMA,
     306        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
     307        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     308        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
     309        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     310        exp.Property: exp.Properties.Location.POST_WITH,
     311        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
     312        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     313        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     314        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     315        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     316        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
     317        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     318        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     319        exp.Set: exp.Properties.Location.POST_SCHEMA,
     320        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     321        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     322        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     323        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     324        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     325        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     326        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
     327        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     328        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
     329        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     330        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     331        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     332        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     333        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
     334    }
     335
     336    # Keywords that can't be used as unquoted identifier names
     337    RESERVED_KEYWORDS: t.Set[str] = set()
     338
     339    # Expressions whose comments are separated from them for better formatting
     340    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     341        exp.Create,
     342        exp.Delete,
     343        exp.Drop,
     344        exp.From,
     345        exp.Insert,
     346        exp.Join,
     347        exp.Select,
     348        exp.Update,
     349        exp.Where,
     350        exp.With,
     351    )
     352
     353    # Expressions that should not have their comments generated in maybe_comment
     354    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     355        exp.Binary,
     356        exp.Union,
     357    )
     358
     359    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
     360    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
     361        exp.Column,
     362        exp.Literal,
     363        exp.Neg,
     364        exp.Paren,
     365    )
     366
     367    # Expressions that need to have all CTEs under them bubbled up to them
     368    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
     369
     370    KEY_VALUE_DEFINITONS = (exp.Bracket, exp.EQ, exp.PropertyEQ, exp.Slice)
     371
     372    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     373
     374    __slots__ = (
     375        "pretty",
     376        "identify",
     377        "normalize",
     378        "pad",
     379        "_indent",
     380        "normalize_functions",
     381        "unsupported_level",
     382        "max_unsupported",
     383        "leading_comma",
     384        "max_text_width",
     385        "comments",
     386        "dialect",
     387        "unsupported_messages",
     388        "_escaped_quote_end",
     389        "_escaped_identifier_end",
     390    )
     391
     392    def __init__(
     393        self,
     394        pretty: t.Optional[bool] = None,
     395        identify: str | bool = False,
     396        normalize: bool = False,
     397        pad: int = 2,
     398        indent: int = 2,
     399        normalize_functions: t.Optional[str | bool] = None,
     400        unsupported_level: ErrorLevel = ErrorLevel.WARN,
     401        max_unsupported: int = 3,
     402        leading_comma: bool = False,
     403        max_text_width: int = 80,
     404        comments: bool = True,
     405        dialect: DialectType = None,
     406    ):
     407        import sqlglot
     408        from sqlglot.dialects import Dialect
     409
     410        self.pretty = pretty if pretty is not None else sqlglot.pretty
     411        self.identify = identify
     412        self.normalize = normalize
     413        self.pad = pad
     414        self._indent = indent
     415        self.unsupported_level = unsupported_level
     416        self.max_unsupported = max_unsupported
     417        self.leading_comma = leading_comma
     418        self.max_text_width = max_text_width
     419        self.comments = comments
     420        self.dialect = Dialect.get_or_raise(dialect)
     421
     422        # This is both a Dialect property and a Generator argument, so we prioritize the latter
     423        self.normalize_functions = (
     424            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
     425        )
     426
     427        self.unsupported_messages: t.List[str] = []
     428        self._escaped_quote_end: str = (
     429            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
     430        )
     431        self._escaped_identifier_end: str = (
     432            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
     433        )
     434
     435    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
     436        """
     437        Generates the SQL string corresponding to the given syntax tree.
     438
     439        Args:
     440            expression: The syntax tree.
     441            copy: Whether or not to copy the expression. The generator performs mutations so
     442                it is safer to copy.
     443
     444        Returns:
     445            The SQL string corresponding to `expression`.
     446        """
     447        if copy:
     448            expression = expression.copy()
     449
     450        expression = self.preprocess(expression)
     451
     452        self.unsupported_messages = []
     453        sql = self.sql(expression).strip()
     454
     455        if self.pretty:
     456            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     457
     458        if self.unsupported_level == ErrorLevel.IGNORE:
     459            return sql
     460
     461        if self.unsupported_level == ErrorLevel.WARN:
     462            for msg in self.unsupported_messages:
     463                logger.warning(msg)
     464        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     465            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     466
     467        return sql
     468
     469    def preprocess(self, expression: exp.Expression) -> exp.Expression:
     470        """Apply generic preprocessing transformations to a given expression."""
     471        if (
     472            not expression.parent
     473            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
     474            and any(node.parent is not expression for node in expression.find_all(exp.With))
     475        ):
     476            from sqlglot.transforms import move_ctes_to_top_level
     477
     478            expression = move_ctes_to_top_level(expression)
     479
     480        if self.ENSURE_BOOLS:
     481            from sqlglot.transforms import ensure_bools
     482
     483            expression = ensure_bools(expression)
     484
     485        return expression
     486
     487    def unsupported(self, message: str) -> None:
     488        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     489            raise UnsupportedError(message)
     490        self.unsupported_messages.append(message)
     491
     492    def sep(self, sep: str = " ") -> str:
     493        return f"{sep.strip()}\n" if self.pretty else sep
     494
     495    def seg(self, sql: str, sep: str = " ") -> str:
     496        return f"{self.sep(sep)}{sql}"
     497
     498    def pad_comment(self, comment: str) -> str:
     499        comment = " " + comment if comment[0].strip() else comment
     500        comment = comment + " " if comment[-1].strip() else comment
     501        return comment
     502
     503    def maybe_comment(
     504        self,
     505        sql: str,
     506        expression: t.Optional[exp.Expression] = None,
     507        comments: t.Optional[t.List[str]] = None,
     508    ) -> str:
     509        comments = (
     510            ((expression and expression.comments) if comments is None else comments)  # type: ignore
     511            if self.comments
     512            else None
     513        )
     514
     515        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
     516            return sql
     517
     518        comments_sql = " ".join(
     519            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     520        )
     521
     522        if not comments_sql:
     523            return sql
     524
     525        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     526            return (
     527                f"{self.sep()}{comments_sql}{sql}"
     528                if sql[0].isspace()
     529                else f"{comments_sql}{self.sep()}{sql}"
     530            )
     531
     532        return f"{sql} {comments_sql}"
     533
     534    def wrap(self, expression: exp.Expression | str) -> str:
     535        this_sql = self.indent(
     536            self.sql(expression)
     537            if isinstance(expression, (exp.Select, exp.Union))
     538            else self.sql(expression, "this"),
     539            level=1,
     540            pad=0,
     541        )
     542        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     543
     544    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     545        original = self.identify
     546        self.identify = False
     547        result = func(*args, **kwargs)
     548        self.identify = original
     549        return result
     550
     551    def normalize_func(self, name: str) -> str:
     552        if self.normalize_functions == "upper" or self.normalize_functions is True:
     553            return name.upper()
     554        if self.normalize_functions == "lower":
     555            return name.lower()
     556        return name
     557
     558    def indent(
     559        self,
     560        sql: str,
     561        level: int = 0,
     562        pad: t.Optional[int] = None,
     563        skip_first: bool = False,
     564        skip_last: bool = False,
     565    ) -> str:
     566        if not self.pretty:
     567            return sql
     568
     569        pad = self.pad if pad is None else pad
     570        lines = sql.split("\n")
     571
     572        return "\n".join(
     573            line
     574            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     575            else f"{' ' * (level * self._indent + pad)}{line}"
     576            for i, line in enumerate(lines)
     577        )
     578
     579    def sql(
     580        self,
     581        expression: t.Optional[str | exp.Expression],
     582        key: t.Optional[str] = None,
     583        comment: bool = True,
     584    ) -> str:
     585        if not expression:
     586            return ""
     587
     588        if isinstance(expression, str):
     589            return expression
     590
     591        if key:
     592            value = expression.args.get(key)
     593            if value:
     594                return self.sql(value)
     595            return ""
     596
     597        transform = self.TRANSFORMS.get(expression.__class__)
     598
     599        if callable(transform):
     600            sql = transform(self, expression)
     601        elif transform:
     602            sql = transform
     603        elif isinstance(expression, exp.Expression):
     604            exp_handler_name = f"{expression.key}_sql"
     605
     606            if hasattr(self, exp_handler_name):
     607                sql = getattr(self, exp_handler_name)(expression)
     608            elif isinstance(expression, exp.Func):
     609                sql = self.function_fallback_sql(expression)
     610            elif isinstance(expression, exp.Property):
     611                sql = self.property_sql(expression)
     612            else:
     613                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     614        else:
     615            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     616
     617        return self.maybe_comment(sql, expression) if self.comments and comment else sql
     618
     619    def uncache_sql(self, expression: exp.Uncache) -> str:
     620        table = self.sql(expression, "this")
     621        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     622        return f"UNCACHE TABLE{exists_sql} {table}"
     623
     624    def cache_sql(self, expression: exp.Cache) -> str:
     625        lazy = " LAZY" if expression.args.get("lazy") else ""
     626        table = self.sql(expression, "this")
     627        options = expression.args.get("options")
     628        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     629        sql = self.sql(expression, "expression")
     630        sql = f" AS{self.sep()}{sql}" if sql else ""
     631        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     632        return self.prepend_ctes(expression, sql)
     633
     634    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     635        if isinstance(expression.parent, exp.Cast):
     636            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     637        default = "DEFAULT " if expression.args.get("default") else ""
     638        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     639
     640    def column_sql(self, expression: exp.Column) -> str:
     641        join_mark = " (+)" if expression.args.get("join_mark") else ""
     642
     643        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
     644            join_mark = ""
     645            self.unsupported("Outer join syntax using the (+) operator is not supported.")
     646
     647        column = ".".join(
     648            self.sql(part)
     649            for part in (
     650                expression.args.get("catalog"),
     651                expression.args.get("db"),
     652                expression.args.get("table"),
     653                expression.args.get("this"),
     654            )
     655            if part
     656        )
     657
     658        return f"{column}{join_mark}"
     659
     660    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     661        this = self.sql(expression, "this")
     662        this = f" {this}" if this else ""
     663        position = self.sql(expression, "position")
     664        return f"{position}{this}"
     665
     666    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     667        column = self.sql(expression, "this")
     668        kind = self.sql(expression, "kind")
     669        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     670        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     671        kind = f"{sep}{kind}" if kind else ""
     672        constraints = f" {constraints}" if constraints else ""
     673        position = self.sql(expression, "position")
     674        position = f" {position}" if position else ""
     675
     676        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
     677            kind = ""
     678
     679        return f"{exists}{column}{kind}{constraints}{position}"
     680
     681    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     682        this = self.sql(expression, "this")
     683        kind_sql = self.sql(expression, "kind").strip()
     684        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     685
     686    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     687        this = self.sql(expression, "this")
     688        if expression.args.get("not_null"):
     689            persisted = " PERSISTED NOT NULL"
     690        elif expression.args.get("persisted"):
     691            persisted = " PERSISTED"
     692        else:
     693            persisted = ""
     694        return f"AS {this}{persisted}"
     695
     696    def autoincrementcolumnconstraint_sql(self, _) -> str:
     697        return self.token_sql(TokenType.AUTO_INCREMENT)
     698
     699    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     700        if isinstance(expression.this, list):
     701            this = self.wrap(self.expressions(expression, key="this", flat=True))
     702        else:
     703            this = self.sql(expression, "this")
     704
     705        return f"COMPRESS {this}"
     706
     707    def generatedasidentitycolumnconstraint_sql(
     708        self, expression: exp.GeneratedAsIdentityColumnConstraint
     709    ) -> str:
     710        this = ""
     711        if expression.this is not None:
     712            on_null = " ON NULL" if expression.args.get("on_null") else ""
     713            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
     714
     715        start = expression.args.get("start")
     716        start = f"START WITH {start}" if start else ""
     717        increment = expression.args.get("increment")
     718        increment = f" INCREMENT BY {increment}" if increment else ""
     719        minvalue = expression.args.get("minvalue")
     720        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
     721        maxvalue = expression.args.get("maxvalue")
     722        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
     723        cycle = expression.args.get("cycle")
     724        cycle_sql = ""
     725
     726        if cycle is not None:
     727            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
     728            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
     729
     730        sequence_opts = ""
     731        if start or increment or cycle_sql:
     732            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
     733            sequence_opts = f" ({sequence_opts.strip()})"
     734
     735        expr = self.sql(expression, "expression")
     736        expr = f"({expr})" if expr else "IDENTITY"
     737
     738        return f"GENERATED{this} AS {expr}{sequence_opts}"
     739
     740    def generatedasrowcolumnconstraint_sql(
     741        self, expression: exp.GeneratedAsRowColumnConstraint
     742    ) -> str:
     743        start = "START" if expression.args["start"] else "END"
     744        hidden = " HIDDEN" if expression.args.get("hidden") else ""
     745        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
     746
     747    def periodforsystemtimeconstraint_sql(
     748        self, expression: exp.PeriodForSystemTimeConstraint
     749    ) -> str:
     750        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
     751
     752    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
     753        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
     754
     755    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
     756        return f"AS {self.sql(expression, 'this')}"
     757
     758    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
     759        desc = expression.args.get("desc")
     760        if desc is not None:
     761            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
     762        return f"PRIMARY KEY"
     763
     764    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
     765        this = self.sql(expression, "this")
     766        this = f" {this}" if this else ""
     767        index_type = expression.args.get("index_type")
     768        index_type = f" USING {index_type}" if index_type else ""
     769        return f"UNIQUE{this}{index_type}"
     770
     771    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
     772        return self.sql(expression, "this")
     773
     774    def create_sql(self, expression: exp.Create) -> str:
     775        kind = self.sql(expression, "kind").upper()
     776        properties = expression.args.get("properties")
     777        properties_locs = self.locate_properties(properties) if properties else defaultdict()
     778
     779        this = self.createable_sql(expression, properties_locs)
     780
     781        properties_sql = ""
     782        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
     783            exp.Properties.Location.POST_WITH
     784        ):
     785            properties_sql = self.sql(
     786                exp.Properties(
     787                    expressions=[
     788                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
     789                        *properties_locs[exp.Properties.Location.POST_WITH],
     790                    ]
     791                )
     792            )
     793
     794        begin = " BEGIN" if expression.args.get("begin") else ""
     795        end = " END" if expression.args.get("end") else ""
     796
     797        expression_sql = self.sql(expression, "expression")
     798        if expression_sql:
     799            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
     800
     801            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
     802                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
     803                    postalias_props_sql = self.properties(
     804                        exp.Properties(
     805                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
     806                        ),
     807                        wrapped=False,
     808                    )
     809                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
     810                else:
     811                    expression_sql = f" AS{expression_sql}"
     812
     813        postindex_props_sql = ""
     814        if properties_locs.get(exp.Properties.Location.POST_INDEX):
     815            postindex_props_sql = self.properties(
     816                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
     817                wrapped=False,
     818                prefix=" ",
     819            )
     820
     821        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
     822        indexes = f" {indexes}" if indexes else ""
     823        index_sql = indexes + postindex_props_sql
     824
     825        replace = " OR REPLACE" if expression.args.get("replace") else ""
     826        unique = " UNIQUE" if expression.args.get("unique") else ""
     827
     828        postcreate_props_sql = ""
     829        if properties_locs.get(exp.Properties.Location.POST_CREATE):
     830            postcreate_props_sql = self.properties(
     831                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
     832                sep=" ",
     833                prefix=" ",
     834                wrapped=False,
     835            )
     836
     837        modifiers = "".join((replace, unique, postcreate_props_sql))
     838
     839        postexpression_props_sql = ""
     840        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
     841            postexpression_props_sql = self.properties(
     842                exp.Properties(
     843                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
     844                ),
     845                sep=" ",
     846                prefix=" ",
     847                wrapped=False,
     848            )
     849
     850        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
     851        no_schema_binding = (
     852            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
     853        )
     854
     855        clone = self.sql(expression, "clone")
     856        clone = f" {clone}" if clone else ""
     857
     858        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
     859        return self.prepend_ctes(expression, expression_sql)
     860
     861    def clone_sql(self, expression: exp.Clone) -> str:
     862        this = self.sql(expression, "this")
     863        shallow = "SHALLOW " if expression.args.get("shallow") else ""
     864        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
     865        return f"{shallow}{keyword} {this}"
     866
     867    def describe_sql(self, expression: exp.Describe) -> str:
     868        return f"DESCRIBE {self.sql(expression, 'this')}"
     869
     870    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
     871        with_ = self.sql(expression, "with")
     872        if with_:
     873            sql = f"{with_}{self.sep()}{sql}"
     874        return sql
     875
     876    def with_sql(self, expression: exp.With) -> str:
     877        sql = self.expressions(expression, flat=True)
     878        recursive = (
     879            "RECURSIVE "
     880            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
     881            else ""
     882        )
     883
     884        return f"WITH {recursive}{sql}"
     885
     886    def cte_sql(self, expression: exp.CTE) -> str:
     887        alias = self.sql(expression, "alias")
     888        return f"{alias} AS {self.wrap(expression)}"
     889
     890    def tablealias_sql(self, expression: exp.TableAlias) -> str:
     891        alias = self.sql(expression, "this")
     892        columns = self.expressions(expression, key="columns", flat=True)
     893        columns = f"({columns})" if columns else ""
     894
     895        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
     896            alias = "_t"
     897
     898        return f"{alias}{columns}"
     899
     900    def bitstring_sql(self, expression: exp.BitString) -> str:
     901        this = self.sql(expression, "this")
     902        if self.dialect.BIT_START:
     903            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
     904        return f"{int(this, 2)}"
     905
     906    def hexstring_sql(self, expression: exp.HexString) -> str:
     907        this = self.sql(expression, "this")
     908        if self.dialect.HEX_START:
     909            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
     910        return f"{int(this, 16)}"
     911
     912    def bytestring_sql(self, expression: exp.ByteString) -> str:
     913        this = self.sql(expression, "this")
     914        if self.dialect.BYTE_START:
     915            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
     916        return this
     917
     918    def rawstring_sql(self, expression: exp.RawString) -> str:
     919        string = self.escape_str(expression.this.replace("\\", "\\\\"))
     920        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
     921
     922    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
     923        this = self.sql(expression, "this")
     924        specifier = self.sql(expression, "expression")
     925        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
     926        return f"{this}{specifier}"
     927
     928    def datatype_sql(self, expression: exp.DataType) -> str:
     929        type_value = expression.this
     930
     931        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
     932            type_sql = self.sql(expression, "kind")
     933        else:
     934            type_sql = (
     935                self.TYPE_MAPPING.get(type_value, type_value.value)
     936                if isinstance(type_value, exp.DataType.Type)
     937                else type_value
     938            )
     939
     940        nested = ""
     941        interior = self.expressions(expression, flat=True)
     942        values = ""
     943
     944        if interior:
     945            if expression.args.get("nested"):
     946                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
     947                if expression.args.get("values") is not None:
     948                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
     949                    values = self.expressions(expression, key="values", flat=True)
     950                    values = f"{delimiters[0]}{values}{delimiters[1]}"
     951            elif type_value == exp.DataType.Type.INTERVAL:
     952                nested = f" {interior}"
     953            else:
     954                nested = f"({interior})"
     955
     956        type_sql = f"{type_sql}{nested}{values}"
     957        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
     958            exp.DataType.Type.TIMETZ,
     959            exp.DataType.Type.TIMESTAMPTZ,
     960        ):
     961            type_sql = f"{type_sql} WITH TIME ZONE"
     962
     963        return type_sql
     964
     965    def directory_sql(self, expression: exp.Directory) -> str:
     966        local = "LOCAL " if expression.args.get("local") else ""
     967        row_format = self.sql(expression, "row_format")
     968        row_format = f" {row_format}" if row_format else ""
     969        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
     970
     971    def delete_sql(self, expression: exp.Delete) -> str:
     972        this = self.sql(expression, "this")
     973        this = f" FROM {this}" if this else ""
     974        using = self.sql(expression, "using")
     975        using = f" USING {using}" if using else ""
     976        where = self.sql(expression, "where")
     977        returning = self.sql(expression, "returning")
     978        limit = self.sql(expression, "limit")
     979        tables = self.expressions(expression, key="tables")
     980        tables = f" {tables}" if tables else ""
     981        if self.RETURNING_END:
     982            expression_sql = f"{this}{using}{where}{returning}{limit}"
     983        else:
     984            expression_sql = f"{returning}{this}{using}{where}{limit}"
     985        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
     986
     987    def drop_sql(self, expression: exp.Drop) -> str:
     988        this = self.sql(expression, "this")
     989        kind = expression.args["kind"]
     990        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
     991        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
     992        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
     993        cascade = " CASCADE" if expression.args.get("cascade") else ""
     994        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
     995        purge = " PURGE" if expression.args.get("purge") else ""
     996        return (
     997            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
     998        )
     999
    1000    def except_sql(self, expression: exp.Except) -> str:
    1001        return self.prepend_ctes(
    1002            expression,
    1003            self.set_operation(expression, self.except_op(expression)),
    1004        )
    1005
    1006    def except_op(self, expression: exp.Except) -> str:
    1007        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
    1008
    1009    def fetch_sql(self, expression: exp.Fetch) -> str:
    1010        direction = expression.args.get("direction")
    1011        direction = f" {direction.upper()}" if direction else ""
    1012        count = expression.args.get("count")
    1013        count = f" {count}" if count else ""
    1014        if expression.args.get("percent"):
    1015            count = f"{count} PERCENT"
    1016        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1017        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
    1018
    1019    def filter_sql(self, expression: exp.Filter) -> str:
    1020        if self.AGGREGATE_FILTER_SUPPORTED:
    1021            this = self.sql(expression, "this")
    1022            where = self.sql(expression, "expression").strip()
    1023            return f"{this} FILTER({where})"
    1024
    1025        agg = expression.this
    1026        agg_arg = agg.this
    1027        cond = expression.expression.this
    1028        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1029        return self.sql(agg)
    1030
    1031    def hint_sql(self, expression: exp.Hint) -> str:
    1032        if not self.QUERY_HINTS:
    1033            self.unsupported("Hints are not supported")
    1034            return ""
    1035
    1036        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
    1037
    1038    def index_sql(self, expression: exp.Index) -> str:
    1039        unique = "UNIQUE " if expression.args.get("unique") else ""
    1040        primary = "PRIMARY " if expression.args.get("primary") else ""
    1041        amp = "AMP " if expression.args.get("amp") else ""
    1042        name = self.sql(expression, "this")
    1043        name = f"{name} " if name else ""
    1044        table = self.sql(expression, "table")
    1045        table = f"{self.INDEX_ON} {table}" if table else ""
    1046        using = self.sql(expression, "using")
    1047        using = f" USING {using}" if using else ""
    1048        index = "INDEX " if not table else ""
    1049        columns = self.expressions(expression, key="columns", flat=True)
    1050        columns = f"({columns})" if columns else ""
    1051        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1052        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1053        where = self.sql(expression, "where")
    1054        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"
    1055
    1056    def identifier_sql(self, expression: exp.Identifier) -> str:
    1057        text = expression.name
    1058        lower = text.lower()
    1059        text = lower if self.normalize and not expression.quoted else text
    1060        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1061        if (
    1062            expression.quoted
    1063            or self.dialect.can_identify(text, self.identify)
    1064            or lower in self.RESERVED_KEYWORDS
    1065            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1066        ):
    1067            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1068        return text
    1069
    1070    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1071        input_format = self.sql(expression, "input_format")
    1072        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1073        output_format = self.sql(expression, "output_format")
    1074        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1075        return self.sep().join((input_format, output_format))
    1076
    1077    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1078        string = self.sql(exp.Literal.string(expression.name))
    1079        return f"{prefix}{string}"
    1080
    1081    def partition_sql(self, expression: exp.Partition) -> str:
    1082        return f"PARTITION({self.expressions(expression, flat=True)})"
    1083
    1084    def properties_sql(self, expression: exp.Properties) -> str:
    1085        root_properties = []
    1086        with_properties = []
    1087
    1088        for p in expression.expressions:
    1089            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1090            if p_loc == exp.Properties.Location.POST_WITH:
    1091                with_properties.append(p)
    1092            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1093                root_properties.append(p)
    1094
    1095        return self.root_properties(
    1096            exp.Properties(expressions=root_properties)
    1097        ) + self.with_properties(exp.Properties(expressions=with_properties))
    1098
    1099    def root_properties(self, properties: exp.Properties) -> str:
    1100        if properties.expressions:
    1101            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1102        return ""
    1103
    1104    def properties(
    1105        self,
    1106        properties: exp.Properties,
    1107        prefix: str = "",
    1108        sep: str = ", ",
    1109        suffix: str = "",
    1110        wrapped: bool = True,
    1111    ) -> str:
    1112        if properties.expressions:
    1113            expressions = self.expressions(properties, sep=sep, indent=False)
    1114            if expressions:
    1115                expressions = self.wrap(expressions) if wrapped else expressions
    1116                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1117        return ""
    1118
    1119    def with_properties(self, properties: exp.Properties) -> str:
    1120        return self.properties(properties, prefix=self.seg("WITH"))
    1121
    1122    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1123        properties_locs = defaultdict(list)
    1124        for p in properties.expressions:
    1125            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1126            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1127                properties_locs[p_loc].append(p)
    1128            else:
    1129                self.unsupported(f"Unsupported property {p.key}")
    1130
    1131        return properties_locs
    1132
    1133    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1134        if isinstance(expression.this, exp.Dot):
    1135            return self.sql(expression, "this")
    1136        return f"'{expression.name}'" if string_key else expression.name
    1137
    1138    def property_sql(self, expression: exp.Property) -> str:
    1139        property_cls = expression.__class__
    1140        if property_cls == exp.Property:
    1141            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1142
    1143        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1144        if not property_name:
    1145            self.unsupported(f"Unsupported property {expression.key}")
    1146
    1147        return f"{property_name}={self.sql(expression, 'this')}"
    1148
    1149    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1150        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1151        options = f" {options}" if options else ""
    1152        return f"LIKE {self.sql(expression, 'this')}{options}"
    1153
    1154    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1155        no = "NO " if expression.args.get("no") else ""
    1156        protection = " PROTECTION" if expression.args.get("protection") else ""
    1157        return f"{no}FALLBACK{protection}"
    1158
    1159    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1160        no = "NO " if expression.args.get("no") else ""
    1161        local = expression.args.get("local")
    1162        local = f"{local} " if local else ""
    1163        dual = "DUAL " if expression.args.get("dual") else ""
    1164        before = "BEFORE " if expression.args.get("before") else ""
    1165        after = "AFTER " if expression.args.get("after") else ""
    1166        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1167
    1168    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1169        freespace = self.sql(expression, "this")
    1170        percent = " PERCENT" if expression.args.get("percent") else ""
    1171        return f"FREESPACE={freespace}{percent}"
    1172
    1173    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1174        if expression.args.get("default"):
    1175            property = "DEFAULT"
    1176        elif expression.args.get("on"):
    1177            property = "ON"
    1178        else:
    1179            property = "OFF"
    1180        return f"CHECKSUM={property}"
    1181
    1182    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1183        if expression.args.get("no"):
    1184            return "NO MERGEBLOCKRATIO"
    1185        if expression.args.get("default"):
    1186            return "DEFAULT MERGEBLOCKRATIO"
    1187
    1188        percent = " PERCENT" if expression.args.get("percent") else ""
    1189        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1190
    1191    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1192        default = expression.args.get("default")
    1193        minimum = expression.args.get("minimum")
    1194        maximum = expression.args.get("maximum")
    1195        if default or minimum or maximum:
    1196            if default:
    1197                prop = "DEFAULT"
    1198            elif minimum:
    1199                prop = "MINIMUM"
    1200            else:
    1201                prop = "MAXIMUM"
    1202            return f"{prop} DATABLOCKSIZE"
    1203        units = expression.args.get("units")
    1204        units = f" {units}" if units else ""
    1205        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1206
    1207    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1208        autotemp = expression.args.get("autotemp")
    1209        always = expression.args.get("always")
    1210        default = expression.args.get("default")
    1211        manual = expression.args.get("manual")
    1212        never = expression.args.get("never")
    1213
    1214        if autotemp is not None:
    1215            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1216        elif always:
    1217            prop = "ALWAYS"
    1218        elif default:
    1219            prop = "DEFAULT"
    1220        elif manual:
    1221            prop = "MANUAL"
    1222        elif never:
    1223            prop = "NEVER"
    1224        return f"BLOCKCOMPRESSION={prop}"
    1225
    1226    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1227        no = expression.args.get("no")
    1228        no = " NO" if no else ""
    1229        concurrent = expression.args.get("concurrent")
    1230        concurrent = " CONCURRENT" if concurrent else ""
    1231
    1232        for_ = ""
    1233        if expression.args.get("for_all"):
    1234            for_ = " FOR ALL"
    1235        elif expression.args.get("for_insert"):
    1236            for_ = " FOR INSERT"
    1237        elif expression.args.get("for_none"):
    1238            for_ = " FOR NONE"
    1239        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
    1240
    1241    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1242        if isinstance(expression.this, list):
    1243            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1244        if expression.this:
    1245            modulus = self.sql(expression, "this")
    1246            remainder = self.sql(expression, "expression")
    1247            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1248
    1249        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1250        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1251        return f"FROM ({from_expressions}) TO ({to_expressions})"
    1252
    1253    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1254        this = self.sql(expression, "this")
    1255
    1256        for_values_or_default = expression.expression
    1257        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1258            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1259        else:
    1260            for_values_or_default = " DEFAULT"
    1261
    1262        return f"PARTITION OF {this}{for_values_or_default}"
    1263
    1264    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1265        kind = expression.args.get("kind")
    1266        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1267        for_or_in = expression.args.get("for_or_in")
    1268        for_or_in = f" {for_or_in}" if for_or_in else ""
    1269        lock_type = expression.args.get("lock_type")
    1270        override = " OVERRIDE" if expression.args.get("override") else ""
    1271        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
    1272
    1273    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1274        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1275        statistics = expression.args.get("statistics")
    1276        statistics_sql = ""
    1277        if statistics is not None:
    1278            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1279        return f"{data_sql}{statistics_sql}"
    1280
    1281    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1282        sql = "WITH(SYSTEM_VERSIONING=ON"
    1283
    1284        if expression.this:
    1285            history_table = self.sql(expression, "this")
    1286            sql = f"{sql}(HISTORY_TABLE={history_table}"
    1287
    1288            if expression.expression:
    1289                data_consistency_check = self.sql(expression, "expression")
    1290                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
    1291
    1292            sql = f"{sql})"
    1293
    1294        return f"{sql})"
    1295
    1296    def insert_sql(self, expression: exp.Insert) -> str:
    1297        overwrite = expression.args.get("overwrite")
    1298
    1299        if isinstance(expression.this, exp.Directory):
    1300            this = " OVERWRITE" if overwrite else " INTO"
    1301        else:
    1302            this = " OVERWRITE TABLE" if overwrite else " INTO"
    1303
    1304        alternative = expression.args.get("alternative")
    1305        alternative = f" OR {alternative}" if alternative else ""
    1306        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1307
    1308        this = f"{this} {self.sql(expression, 'this')}"
    1309
    1310        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1311        partition_sql = (
    1312            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1313        )
    1314        where = self.sql(expression, "where")
    1315        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1316        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1317        conflict = self.sql(expression, "conflict")
    1318        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1319        returning = self.sql(expression, "returning")
    1320
    1321        if self.RETURNING_END:
    1322            expression_sql = f"{expression_sql}{conflict}{returning}"
    1323        else:
    1324            expression_sql = f"{returning}{expression_sql}{conflict}"
    1325
    1326        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1327        return self.prepend_ctes(expression, sql)
    1328
    1329    def intersect_sql(self, expression: exp.Intersect) -> str:
    1330        return self.prepend_ctes(
    1331            expression,
    1332            self.set_operation(expression, self.intersect_op(expression)),
    1333        )
    1334
    1335    def intersect_op(self, expression: exp.Intersect) -> str:
    1336        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
    1337
    1338    def introducer_sql(self, expression: exp.Introducer) -> str:
    1339        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1340
    1341    def kill_sql(self, expression: exp.Kill) -> str:
    1342        kind = self.sql(expression, "kind")
    1343        kind = f" {kind}" if kind else ""
    1344        this = self.sql(expression, "this")
    1345        this = f" {this}" if this else ""
    1346        return f"KILL{kind}{this}"
    1347
    1348    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1349        return expression.name.upper()
    1350
    1351    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1352        return expression.name.upper()
    1353
    1354    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1355        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1356        constraint = self.sql(expression, "constraint")
    1357        if constraint:
    1358            constraint = f"ON CONSTRAINT {constraint}"
    1359        key = self.expressions(expression, key="key", flat=True)
    1360        do = "" if expression.args.get("duplicate") else " DO "
    1361        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1362        expressions = self.expressions(expression, flat=True)
    1363        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1364        if expressions:
    1365            expressions = f"UPDATE {set_keyword}{expressions}"
    1366        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
    1367
    1368    def returning_sql(self, expression: exp.Returning) -> str:
    1369        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1370
    1371    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1372        fields = expression.args.get("fields")
    1373        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1374        escaped = expression.args.get("escaped")
    1375        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1376        items = expression.args.get("collection_items")
    1377        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1378        keys = expression.args.get("map_keys")
    1379        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1380        lines = expression.args.get("lines")
    1381        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1382        null = expression.args.get("null")
    1383        null = f" NULL DEFINED AS {null}" if null else ""
    1384        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1385
    1386    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1387        return f"WITH ({self.expressions(expression, flat=True)})"
    1388
    1389    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1390        this = f"{self.sql(expression, 'this')} INDEX"
    1391        target = self.sql(expression, "target")
    1392        target = f" FOR {target}" if target else ""
    1393        return f"{this}{target} ({self.expressions(expression, flat=True)})"
    1394
    1395    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1396        this = self.sql(expression, "this")
    1397        kind = self.sql(expression, "kind")
    1398        expr = self.sql(expression, "expression")
    1399        return f"{this} ({kind} => {expr})"
    1400
    1401    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1402        table = ".".join(
    1403            self.sql(part)
    1404            for part in (
    1405                expression.args.get("catalog"),
    1406                expression.args.get("db"),
    1407                expression.args.get("this"),
    1408            )
    1409            if part is not None
    1410        )
    1411
    1412        version = self.sql(expression, "version")
    1413        version = f" {version}" if version else ""
    1414        alias = self.sql(expression, "alias")
    1415        alias = f"{sep}{alias}" if alias else ""
    1416        hints = self.expressions(expression, key="hints", sep=" ")
    1417        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1418        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1419        pivots = f" {pivots}" if pivots else ""
    1420        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
    1421        laterals = self.expressions(expression, key="laterals", sep="")
    1422
    1423        file_format = self.sql(expression, "format")
    1424        if file_format:
    1425            pattern = self.sql(expression, "pattern")
    1426            pattern = f", PATTERN => {pattern}" if pattern else ""
    1427            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1428
    1429        index = self.sql(expression, "index")
    1430        index = f" AT {index}" if index else ""
    1431
    1432        ordinality = expression.args.get("ordinality") or ""
    1433        if ordinality:
    1434            ordinality = f" WITH ORDINALITY{alias}"
    1435            alias = ""
    1436
    1437        when = self.sql(expression, "when")
    1438        if when:
    1439            table = f"{table} {when}"
    1440
    1441        return f"{table}{version}{file_format}{alias}{index}{hints}{pivots}{joins}{laterals}{ordinality}"
    1442
    1443    def tablesample_sql(
    1444        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1445    ) -> str:
    1446        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1447            table = expression.this.copy()
    1448            table.set("alias", None)
    1449            this = self.sql(table)
    1450            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1451        else:
    1452            this = self.sql(expression, "this")
    1453            alias = ""
    1454
    1455        method = self.sql(expression, "method")
    1456        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1457        numerator = self.sql(expression, "bucket_numerator")
    1458        denominator = self.sql(expression, "bucket_denominator")
    1459        field = self.sql(expression, "bucket_field")
    1460        field = f" ON {field}" if field else ""
    1461        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1462        percent = self.sql(expression, "percent")
    1463        percent = f"{percent} PERCENT" if percent else ""
    1464        rows = self.sql(expression, "rows")
    1465        rows = f"{rows} ROWS" if rows else ""
    1466
    1467        size = self.sql(expression, "size")
    1468        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1469            size = f"{size} PERCENT"
    1470
    1471        seed = self.sql(expression, "seed")
    1472        seed = f" {seed_prefix} ({seed})" if seed else ""
    1473        kind = expression.args.get("kind", "TABLESAMPLE")
    1474
    1475        expr = f"{bucket}{percent}{rows}{size}"
    1476        if self.TABLESAMPLE_REQUIRES_PARENS:
    1477            expr = f"({expr})"
    1478
    1479        return f"{this} {kind} {method}{expr}{seed}{alias}"
    1480
    1481    def pivot_sql(self, expression: exp.Pivot) -> str:
    1482        expressions = self.expressions(expression, flat=True)
    1483
    1484        if expression.this:
    1485            this = self.sql(expression, "this")
    1486            if not expressions:
    1487                return f"UNPIVOT {this}"
    1488
    1489            on = f"{self.seg('ON')} {expressions}"
    1490            using = self.expressions(expression, key="using", flat=True)
    1491            using = f"{self.seg('USING')} {using}" if using else ""
    1492            group = self.sql(expression, "group")
    1493            return f"PIVOT {this}{on}{using}{group}"
    1494
    1495        alias = self.sql(expression, "alias")
    1496        alias = f" AS {alias}" if alias else ""
    1497        unpivot = expression.args.get("unpivot")
    1498        direction = "UNPIVOT" if unpivot else "PIVOT"
    1499        field = self.sql(expression, "field")
    1500        include_nulls = expression.args.get("include_nulls")
    1501        if include_nulls is not None:
    1502            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1503        else:
    1504            nulls = ""
    1505        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
    1506
    1507    def version_sql(self, expression: exp.Version) -> str:
    1508        this = f"FOR {expression.name}"
    1509        kind = expression.text("kind")
    1510        expr = self.sql(expression, "expression")
    1511        return f"{this} {kind} {expr}"
    1512
    1513    def tuple_sql(self, expression: exp.Tuple) -> str:
    1514        return f"({self.expressions(expression, flat=True)})"
    1515
    1516    def update_sql(self, expression: exp.Update) -> str:
    1517        this = self.sql(expression, "this")
    1518        set_sql = self.expressions(expression, flat=True)
    1519        from_sql = self.sql(expression, "from")
    1520        where_sql = self.sql(expression, "where")
    1521        returning = self.sql(expression, "returning")
    1522        order = self.sql(expression, "order")
    1523        limit = self.sql(expression, "limit")
    1524        if self.RETURNING_END:
    1525            expression_sql = f"{from_sql}{where_sql}{returning}"
    1526        else:
    1527            expression_sql = f"{returning}{from_sql}{where_sql}"
    1528        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1529        return self.prepend_ctes(expression, sql)
    1530
    1531    def values_sql(self, expression: exp.Values) -> str:
    1532        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1533        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
    1534            args = self.expressions(expression)
    1535            alias = self.sql(expression, "alias")
    1536            values = f"VALUES{self.seg('')}{args}"
    1537            values = (
    1538                f"({values})"
    1539                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1540                else values
    1541            )
    1542            return f"{values} AS {alias}" if alias else values
    1543
    1544        # Converts `VALUES...` expression into a series of select unions.
    1545        alias_node = expression.args.get("alias")
    1546        column_names = alias_node and alias_node.columns
    1547
    1548        selects: t.List[exp.Subqueryable] = []
    1549
    1550        for i, tup in enumerate(expression.expressions):
    1551            row = tup.expressions
    1552
    1553            if i == 0 and column_names:
    1554                row = [
    1555                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1556                ]
    1557
    1558            selects.append(exp.Select(expressions=row))
    1559
    1560        if self.pretty:
    1561            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1562            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1563            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1564            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1565            return self.subquery_sql(
    1566                subqueryable.subquery(alias_node and alias_node.this, copy=False)
    1567            )
    1568
    1569        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1570        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1571        return f"({unions}){alias}"
    1572
    1573    def var_sql(self, expression: exp.Var) -> str:
    1574        return self.sql(expression, "this")
    1575
    1576    def into_sql(self, expression: exp.Into) -> str:
    1577        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1578        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    1579        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    1580
    1581    def from_sql(self, expression: exp.From) -> str:
    1582        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    1583
    1584    def group_sql(self, expression: exp.Group) -> str:
    1585        group_by = self.op_expressions("GROUP BY", expression)
    1586
    1587        if expression.args.get("all"):
    1588            return f"{group_by} ALL"
    1589
    1590        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1591        grouping_sets = (
    1592            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1593        )
    1594
    1595        cube = expression.args.get("cube", [])
    1596        if seq_get(cube, 0) is True:
    1597            return f"{group_by}{self.seg('WITH CUBE')}"
    1598        else:
    1599            cube_sql = self.expressions(expression, key="cube", indent=False)
    1600            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1601
    1602        rollup = expression.args.get("rollup", [])
    1603        if seq_get(rollup, 0) is True:
    1604            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1605        else:
    1606            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1607            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1608
    1609        groupings = csv(
    1610            grouping_sets,
    1611            cube_sql,
    1612            rollup_sql,
    1613            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1614            sep=self.GROUPINGS_SEP,
    1615        )
    1616
    1617        if expression.args.get("expressions") and groupings:
    1618            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1619
    1620        return f"{group_by}{groupings}"
    1621
    1622    def having_sql(self, expression: exp.Having) -> str:
    1623        this = self.indent(self.sql(expression, "this"))
    1624        return f"{self.seg('HAVING')}{self.sep()}{this}"
    1625
    1626    def connect_sql(self, expression: exp.Connect) -> str:
    1627        start = self.sql(expression, "start")
    1628        start = self.seg(f"START WITH {start}") if start else ""
    1629        connect = self.sql(expression, "connect")
    1630        connect = self.seg(f"CONNECT BY {connect}")
    1631        return start + connect
    1632
    1633    def prior_sql(self, expression: exp.Prior) -> str:
    1634        return f"PRIOR {self.sql(expression, 'this')}"
    1635
    1636    def join_sql(self, expression: exp.Join) -> str:
    1637        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1638            side = None
    1639        else:
    1640            side = expression.side
    1641
    1642        op_sql = " ".join(
    1643            op
    1644            for op in (
    1645                expression.method,
    1646                "GLOBAL" if expression.args.get("global") else None,
    1647                side,
    1648                expression.kind,
    1649                expression.hint if self.JOIN_HINTS else None,
    1650            )
    1651            if op
    1652        )
    1653        on_sql = self.sql(expression, "on")
    1654        using = expression.args.get("using")
    1655
    1656        if not on_sql and using:
    1657            on_sql = csv(*(self.sql(column) for column in using))
    1658
    1659        this_sql = self.sql(expression, "this")
    1660
    1661        if on_sql:
    1662            on_sql = self.indent(on_sql, skip_first=True)
    1663            space = self.seg(" " * self.pad) if self.pretty else " "
    1664            if using:
    1665                on_sql = f"{space}USING ({on_sql})"
    1666            else:
    1667                on_sql = f"{space}ON {on_sql}"
    1668        elif not op_sql:
    1669            return f", {this_sql}"
    1670
    1671        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1672        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
    1673
    1674    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    1675        args = self.expressions(expression, flat=True)
    1676        args = f"({args})" if len(args.split(",")) > 1 else args
    1677        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    1678
    1679    def lateral_sql(self, expression: exp.Lateral) -> str:
    1680        this = self.sql(expression, "this")
    1681
    1682        if expression.args.get("view"):
    1683            alias = expression.args["alias"]
    1684            columns = self.expressions(alias, key="columns", flat=True)
    1685            table = f" {alias.name}" if alias.name else ""
    1686            columns = f" AS {columns}" if columns else ""
    1687            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1688            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1689
    1690        alias = self.sql(expression, "alias")
    1691        alias = f" AS {alias}" if alias else ""
    1692        return f"LATERAL {this}{alias}"
    1693
    1694    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1695        this = self.sql(expression, "this")
    1696
    1697        args = [
    1698            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    1699            for e in (expression.args.get(k) for k in ("offset", "expression"))
    1700            if e
    1701        ]
    1702
    1703        args_sql = ", ".join(self.sql(e) for e in args)
    1704        args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql
    1705        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}"
    1706
    1707    def offset_sql(self, expression: exp.Offset) -> str:
    1708        this = self.sql(expression, "this")
    1709        expression = expression.expression
    1710        expression = (
    1711            self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression
    1712        )
    1713        return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"
    1714
    1715    def setitem_sql(self, expression: exp.SetItem) -> str:
    1716        kind = self.sql(expression, "kind")
    1717        kind = f"{kind} " if kind else ""
    1718        this = self.sql(expression, "this")
    1719        expressions = self.expressions(expression)
    1720        collate = self.sql(expression, "collate")
    1721        collate = f" COLLATE {collate}" if collate else ""
    1722        global_ = "GLOBAL " if expression.args.get("global") else ""
    1723        return f"{global_}{kind}{this}{expressions}{collate}"
    1724
    1725    def set_sql(self, expression: exp.Set) -> str:
    1726        expressions = (
    1727            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1728        )
    1729        tag = " TAG" if expression.args.get("tag") else ""
    1730        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
    1731
    1732    def pragma_sql(self, expression: exp.Pragma) -> str:
    1733        return f"PRAGMA {self.sql(expression, 'this')}"
    1734
    1735    def lock_sql(self, expression: exp.Lock) -> str:
    1736        if not self.LOCKING_READS_SUPPORTED:
    1737            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1738            return ""
    1739
    1740        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1741        expressions = self.expressions(expression, flat=True)
    1742        expressions = f" OF {expressions}" if expressions else ""
    1743        wait = expression.args.get("wait")
    1744
    1745        if wait is not None:
    1746            if isinstance(wait, exp.Literal):
    1747                wait = f" WAIT {self.sql(wait)}"
    1748            else:
    1749                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1750
    1751        return f"{lock_type}{expressions}{wait or ''}"
    1752
    1753    def literal_sql(self, expression: exp.Literal) -> str:
    1754        text = expression.this or ""
    1755        if expression.is_string:
    1756            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    1757        return text
    1758
    1759    def escape_str(self, text: str) -> str:
    1760        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
    1761        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
    1762            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1763        elif self.pretty:
    1764            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1765        return text
    1766
    1767    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1768        local = " LOCAL" if expression.args.get("local") else ""
    1769        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1770        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1771        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1772        partition = self.sql(expression, "partition")
    1773        partition = f" {partition}" if partition else ""
    1774        input_format = self.sql(expression, "input_format")
    1775        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1776        serde = self.sql(expression, "serde")
    1777        serde = f" SERDE {serde}" if serde else ""
    1778        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    1779
    1780    def null_sql(self, *_) -> str:
    1781        return "NULL"
    1782
    1783    def boolean_sql(self, expression: exp.Boolean) -> str:
    1784        return "TRUE" if expression.this else "FALSE"
    1785
    1786    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1787        this = self.sql(expression, "this")
    1788        this = f"{this} " if this else this
    1789        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
    1790
    1791    def cluster_sql(self, expression: exp.Cluster) -> str:
    1792        return self.op_expressions("CLUSTER BY", expression)
    1793
    1794    def distribute_sql(self, expression: exp.Distribute) -> str:
    1795        return self.op_expressions("DISTRIBUTE BY", expression)
    1796
    1797    def sort_sql(self, expression: exp.Sort) -> str:
    1798        return self.op_expressions("SORT BY", expression)
    1799
    1800    def ordered_sql(self, expression: exp.Ordered) -> str:
    1801        desc = expression.args.get("desc")
    1802        asc = not desc
    1803
    1804        nulls_first = expression.args.get("nulls_first")
    1805        nulls_last = not nulls_first
    1806        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    1807        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    1808        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    1809
    1810        this = self.sql(expression, "this")
    1811
    1812        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    1813        nulls_sort_change = ""
    1814        if nulls_first and (
    1815            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1816        ):
    1817            nulls_sort_change = " NULLS FIRST"
    1818        elif (
    1819            nulls_last
    1820            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1821            and not nulls_are_last
    1822        ):
    1823            nulls_sort_change = " NULLS LAST"
    1824
    1825        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    1826        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1827            null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    1828            this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    1829            nulls_sort_change = ""
    1830
    1831        return f"{this}{sort_order}{nulls_sort_change}"
    1832
    1833    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1834        partition = self.partition_by_sql(expression)
    1835        order = self.sql(expression, "order")
    1836        measures = self.expressions(expression, key="measures")
    1837        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1838        rows = self.sql(expression, "rows")
    1839        rows = self.seg(rows) if rows else ""
    1840        after = self.sql(expression, "after")
    1841        after = self.seg(after) if after else ""
    1842        pattern = self.sql(expression, "pattern")
    1843        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1844        definition_sqls = [
    1845            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1846            for definition in expression.args.get("define", [])
    1847        ]
    1848        definitions = self.expressions(sqls=definition_sqls)
    1849        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1850        body = "".join(
    1851            (
    1852                partition,
    1853                order,
    1854                measures,
    1855                rows,
    1856                after,
    1857                pattern,
    1858                define,
    1859            )
    1860        )
    1861        alias = self.sql(expression, "alias")
    1862        alias = f" {alias}" if alias else ""
    1863        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    1864
    1865    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1866        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
    1867
    1868        # If the limit is generated as TOP, we need to ensure it's not generated twice
    1869        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
    1870
    1871        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1872            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    1873        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1874            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    1875
    1876        fetch = isinstance(limit, exp.Fetch)
    1877
    1878        offset_limit_modifiers = (
    1879            self.offset_limit_modifiers(expression, fetch, limit)
    1880            if with_offset_limit_modifiers
    1881            else []
    1882        )
    1883
    1884        return csv(
    1885            *sqls,
    1886            *[self.sql(join) for join in expression.args.get("joins") or []],
    1887            self.sql(expression, "connect"),
    1888            self.sql(expression, "match"),
    1889            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1890            self.sql(expression, "where"),
    1891            self.sql(expression, "group"),
    1892            self.sql(expression, "having"),
    1893            *self.after_having_modifiers(expression),
    1894            self.sql(expression, "order"),
    1895            *offset_limit_modifiers,
    1896            *self.after_limit_modifiers(expression),
    1897            sep="",
    1898        )
    1899
    1900    def offset_limit_modifiers(
    1901        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    1902    ) -> t.List[str]:
    1903        return [
    1904            self.sql(expression, "offset") if fetch else self.sql(limit),
    1905            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1906        ]
    1907
    1908    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1909        return [
    1910            self.sql(expression, "qualify"),
    1911            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1912            if expression.args.get("windows")
    1913            else "",
    1914            self.sql(expression, "distribute"),
    1915            self.sql(expression, "sort"),
    1916            self.sql(expression, "cluster"),
    1917        ]
    1918
    1919    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1920        locks = self.expressions(expression, key="locks", sep=" ")
    1921        locks = f" {locks}" if locks else ""
    1922        return [locks, self.sql(expression, "sample")]
    1923
    1924    def select_sql(self, expression: exp.Select) -> str:
    1925        hint = self.sql(expression, "hint")
    1926        distinct = self.sql(expression, "distinct")
    1927        distinct = f" {distinct}" if distinct else ""
    1928        kind = self.sql(expression, "kind").upper()
    1929        limit = expression.args.get("limit")
    1930        top = (
    1931            self.limit_sql(limit, top=True)
    1932            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
    1933            else ""
    1934        )
    1935
    1936        expressions = self.expressions(expression)
    1937
    1938        if kind:
    1939            if kind in self.SELECT_KINDS:
    1940                kind = f" AS {kind}"
    1941            else:
    1942                if kind == "STRUCT":
    1943                    expressions = self.expressions(
    1944                        sqls=[
    1945                            self.sql(
    1946                                exp.Struct(
    1947                                    expressions=[
    1948                                        exp.column(e.output_name).eq(
    1949                                            e.this if isinstance(e, exp.Alias) else e
    1950                                        )
    1951                                        for e in expression.expressions
    1952                                    ]
    1953                                )
    1954                            )
    1955                        ]
    1956                    )
    1957                kind = ""
    1958
    1959        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    1960        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    1961        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    1962        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1963        sql = self.query_modifiers(
    1964            expression,
    1965            f"SELECT{top_distinct}{kind}{expressions}",
    1966            self.sql(expression, "into", comment=False),
    1967            self.sql(expression, "from", comment=False),
    1968        )
    1969        return self.prepend_ctes(expression, sql)
    1970
    1971    def schema_sql(self, expression: exp.Schema) -> str:
    1972        this = self.sql(expression, "this")
    1973        sql = self.schema_columns_sql(expression)
    1974        return f"{this} {sql}" if this and sql else this or sql
    1975
    1976    def schema_columns_sql(self, expression: exp.Schema) -> str:
    1977        if expression.expressions:
    1978            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1979        return ""
    1980
    1981    def star_sql(self, expression: exp.Star) -> str:
    1982        except_ = self.expressions(expression, key="except", flat=True)
    1983        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1984        replace = self.expressions(expression, key="replace", flat=True)
    1985        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1986        return f"*{except_}{replace}"
    1987
    1988    def parameter_sql(self, expression: exp.Parameter) -> str:
    1989        this = self.sql(expression, "this")
    1990        return f"{self.PARAMETER_TOKEN}{this}"
    1991
    1992    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1993        this = self.sql(expression, "this")
    1994        kind = expression.text("kind")
    1995        if kind:
    1996            kind = f"{kind}."
    1997        return f"@@{kind}{this}"
    1998
    1999    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2000        return f":{expression.name}" if expression.name else "?"
    2001
    2002    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2003        alias = self.sql(expression, "alias")
    2004        alias = f"{sep}{alias}" if alias else ""
    2005
    2006        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2007        pivots = f" {pivots}" if pivots else ""
    2008
    2009        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2010        return self.prepend_ctes(expression, sql)
    2011
    2012    def qualify_sql(self, expression: exp.Qualify) -> str:
    2013        this = self.indent(self.sql(expression, "this"))
    2014        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    2015
    2016    def union_sql(self, expression: exp.Union) -> str:
    2017        return self.prepend_ctes(
    2018            expression,
    2019            self.set_operation(expression, self.union_op(expression)),
    2020        )
    2021
    2022    def union_op(self, expression: exp.Union) -> str:
    2023        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2024        kind = kind if expression.args.get("distinct") else " ALL"
    2025        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2026        return f"UNION{kind}{by_name}"
    2027
    2028    def unnest_sql(self, expression: exp.Unnest) -> str:
    2029        args = self.expressions(expression, flat=True)
    2030
    2031        alias = expression.args.get("alias")
    2032        offset = expression.args.get("offset")
    2033
    2034        if self.UNNEST_WITH_ORDINALITY:
    2035            if alias and isinstance(offset, exp.Expression):
    2036                alias.append("columns", offset)
    2037
    2038        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2039            columns = alias.columns
    2040            alias = self.sql(columns[0]) if columns else ""
    2041        else:
    2042            alias = self.sql(alias)
    2043
    2044        alias = f" AS {alias}" if alias else alias
    2045        if self.UNNEST_WITH_ORDINALITY:
    2046            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2047        else:
    2048            if isinstance(offset, exp.Expression):
    2049                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2050            elif offset:
    2051                suffix = f"{alias} WITH OFFSET"
    2052            else:
    2053                suffix = alias
    2054
    2055        return f"UNNEST({args}){suffix}"
    2056
    2057    def where_sql(self, expression: exp.Where) -> str:
    2058        this = self.indent(self.sql(expression, "this"))
    2059        return f"{self.seg('WHERE')}{self.sep()}{this}"
    2060
    2061    def window_sql(self, expression: exp.Window) -> str:
    2062        this = self.sql(expression, "this")
    2063        partition = self.partition_by_sql(expression)
    2064        order = expression.args.get("order")
    2065        order = self.order_sql(order, flat=True) if order else ""
    2066        spec = self.sql(expression, "spec")
    2067        alias = self.sql(expression, "alias")
    2068        over = self.sql(expression, "over") or "OVER"
    2069
    2070        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2071
    2072        first = expression.args.get("first")
    2073        if first is None:
    2074            first = ""
    2075        else:
    2076            first = "FIRST" if first else "LAST"
    2077
    2078        if not partition and not order and not spec and alias:
    2079            return f"{this} {alias}"
    2080
    2081        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2082        return f"{this} ({args})"
    2083
    2084    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2085        partition = self.expressions(expression, key="partition_by", flat=True)
    2086        return f"PARTITION BY {partition}" if partition else ""
    2087
    2088    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2089        kind = self.sql(expression, "kind")
    2090        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2091        end = (
    2092            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2093            or "CURRENT ROW"
    2094        )
    2095        return f"{kind} BETWEEN {start} AND {end}"
    2096
    2097    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2098        this = self.sql(expression, "this")
    2099        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2100        return f"{this} WITHIN GROUP ({expression_sql})"
    2101
    2102    def between_sql(self, expression: exp.Between) -> str:
    2103        this = self.sql(expression, "this")
    2104        low = self.sql(expression, "low")
    2105        high = self.sql(expression, "high")
    2106        return f"{this} BETWEEN {low} AND {high}"
    2107
    2108    def bracket_sql(self, expression: exp.Bracket) -> str:
    2109        expressions = apply_index_offset(
    2110            expression.this,
    2111            expression.expressions,
    2112            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2113        )
    2114        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2115        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    2116
    2117    def all_sql(self, expression: exp.All) -> str:
    2118        return f"ALL {self.wrap(expression)}"
    2119
    2120    def any_sql(self, expression: exp.Any) -> str:
    2121        this = self.sql(expression, "this")
    2122        if isinstance(expression.this, exp.Subqueryable):
    2123            this = self.wrap(this)
    2124        return f"ANY {this}"
    2125
    2126    def exists_sql(self, expression: exp.Exists) -> str:
    2127        return f"EXISTS{self.wrap(expression)}"
    2128
    2129    def case_sql(self, expression: exp.Case) -> str:
    2130        this = self.sql(expression, "this")
    2131        statements = [f"CASE {this}" if this else "CASE"]
    2132
    2133        for e in expression.args["ifs"]:
    2134            statements.append(f"WHEN {self.sql(e, 'this')}")
    2135            statements.append(f"THEN {self.sql(e, 'true')}")
    2136
    2137        default = self.sql(expression, "default")
    2138
    2139        if default:
    2140            statements.append(f"ELSE {default}")
    2141
    2142        statements.append("END")
    2143
    2144        if self.pretty and self.text_width(statements) > self.max_text_width:
    2145            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2146
    2147        return " ".join(statements)
    2148
    2149    def constraint_sql(self, expression: exp.Constraint) -> str:
    2150        this = self.sql(expression, "this")
    2151        expressions = self.expressions(expression, flat=True)
    2152        return f"CONSTRAINT {this} {expressions}"
    2153
    2154    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2155        order = expression.args.get("order")
    2156        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2157        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    2158
    2159    def extract_sql(self, expression: exp.Extract) -> str:
    2160        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2161        expression_sql = self.sql(expression, "expression")
    2162        return f"EXTRACT({this} FROM {expression_sql})"
    2163
    2164    def trim_sql(self, expression: exp.Trim) -> str:
    2165        trim_type = self.sql(expression, "position")
    2166
    2167        if trim_type == "LEADING":
    2168            return self.func("LTRIM", expression.this)
    2169        elif trim_type == "TRAILING":
    2170            return self.func("RTRIM", expression.this)
    2171        else:
    2172            return self.func("TRIM", expression.this, expression.expression)
    2173
    2174    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2175        args = expression.expressions
    2176        if isinstance(expression, exp.ConcatWs):
    2177            args = args[1:]  # Skip the delimiter
    2178
    2179        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2180            args = [exp.cast(e, "text") for e in args]
    2181
    2182        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2183            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2184
    2185        return args
    2186
    2187    def concat_sql(self, expression: exp.Concat) -> str:
    2188        expressions = self.convert_concat_args(expression)
    2189
    2190        # Some dialects don't allow a single-argument CONCAT call
    2191        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2192            return self.sql(expressions[0])
    2193
    2194        return self.func("CONCAT", *expressions)
    2195
    2196    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2197        return self.func(
    2198            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2199        )
    2200
    2201    def check_sql(self, expression: exp.Check) -> str:
    2202        this = self.sql(expression, key="this")
    2203        return f"CHECK ({this})"
    2204
    2205    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2206        expressions = self.expressions(expression, flat=True)
    2207        reference = self.sql(expression, "reference")
    2208        reference = f" {reference}" if reference else ""
    2209        delete = self.sql(expression, "delete")
    2210        delete = f" ON DELETE {delete}" if delete else ""
    2211        update = self.sql(expression, "update")
    2212        update = f" ON UPDATE {update}" if update else ""
    2213        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
    2214
    2215    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2216        expressions = self.expressions(expression, flat=True)
    2217        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2218        options = f" {options}" if options else ""
    2219        return f"PRIMARY KEY ({expressions}){options}"
    2220
    2221    def if_sql(self, expression: exp.If) -> str:
    2222        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
    2223
    2224    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2225        modifier = expression.args.get("modifier")
    2226        modifier = f" {modifier}" if modifier else ""
    2227        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    2228
    2229    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2230        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
    2231
    2232    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2233        return f"{self.sql(expression, 'this')} FORMAT JSON"
    2234
    2235    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    2236        null_handling = expression.args.get("null_handling")
    2237        null_handling = f" {null_handling}" if null_handling else ""
    2238        unique_keys = expression.args.get("unique_keys")
    2239        if unique_keys is not None:
    2240            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2241        else:
    2242            unique_keys = ""
    2243        return_type = self.sql(expression, "return_type")
    2244        return_type = f" RETURNING {return_type}" if return_type else ""
    2245        encoding = self.sql(expression, "encoding")
    2246        encoding = f" ENCODING {encoding}" if encoding else ""
    2247        return self.func(
    2248            "JSON_OBJECT",
    2249            *expression.expressions,
    2250            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2251        )
    2252
    2253    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2254        null_handling = expression.args.get("null_handling")
    2255        null_handling = f" {null_handling}" if null_handling else ""
    2256        return_type = self.sql(expression, "return_type")
    2257        return_type = f" RETURNING {return_type}" if return_type else ""
    2258        strict = " STRICT" if expression.args.get("strict") else ""
    2259        return self.func(
    2260            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2261        )
    2262
    2263    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2264        this = self.sql(expression, "this")
    2265        order = self.sql(expression, "order")
    2266        null_handling = expression.args.get("null_handling")
    2267        null_handling = f" {null_handling}" if null_handling else ""
    2268        return_type = self.sql(expression, "return_type")
    2269        return_type = f" RETURNING {return_type}" if return_type else ""
    2270        strict = " STRICT" if expression.args.get("strict") else ""
    2271        return self.func(
    2272            "JSON_ARRAYAGG",
    2273            this,
    2274            suffix=f"{order}{null_handling}{return_type}{strict})",
    2275        )
    2276
    2277    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2278        path = self.sql(expression, "path")
    2279        path = f" PATH {path}" if path else ""
    2280        nested_schema = self.sql(expression, "nested_schema")
    2281
    2282        if nested_schema:
    2283            return f"NESTED{path} {nested_schema}"
    2284
    2285        this = self.sql(expression, "this")
    2286        kind = self.sql(expression, "kind")
    2287        kind = f" {kind}" if kind else ""
    2288        return f"{this}{kind}{path}"
    2289
    2290    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    2291        return self.func("COLUMNS", *expression.expressions)
    2292
    2293    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2294        this = self.sql(expression, "this")
    2295        path = self.sql(expression, "path")
    2296        path = f", {path}" if path else ""
    2297        error_handling = expression.args.get("error_handling")
    2298        error_handling = f" {error_handling}" if error_handling else ""
    2299        empty_handling = expression.args.get("empty_handling")
    2300        empty_handling = f" {empty_handling}" if empty_handling else ""
    2301        schema = self.sql(expression, "schema")
    2302        return self.func(
    2303            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2304        )
    2305
    2306    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2307        this = self.sql(expression, "this")
    2308        kind = self.sql(expression, "kind")
    2309        path = self.sql(expression, "path")
    2310        path = f" {path}" if path else ""
    2311        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2312        return f"{this} {kind}{path}{as_json}"
    2313
    2314    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2315        this = self.sql(expression, "this")
    2316        path = self.sql(expression, "path")
    2317        path = f", {path}" if path else ""
    2318        expressions = self.expressions(expression)
    2319        with_ = (
    2320            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2321            if expressions
    2322            else ""
    2323        )
    2324        return f"OPENJSON({this}{path}){with_}"
    2325
    2326    def in_sql(self, expression: exp.In) -> str:
    2327        query = expression.args.get("query")
    2328        unnest = expression.args.get("unnest")
    2329        field = expression.args.get("field")
    2330        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2331
    2332        if query:
    2333            in_sql = self.wrap(query)
    2334        elif unnest:
    2335            in_sql = self.in_unnest_op(unnest)
    2336        elif field:
    2337            in_sql = self.sql(field)
    2338        else:
    2339            in_sql = f"({self.expressions(expression, flat=True)})"
    2340
    2341        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    2342
    2343    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    2344        return f"(SELECT {self.sql(unnest)})"
    2345
    2346    def interval_sql(self, expression: exp.Interval) -> str:
    2347        unit = self.sql(expression, "unit")
    2348        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2349            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    2350        unit = f" {unit}" if unit else ""
    2351
    2352        if self.SINGLE_STRING_INTERVAL:
    2353            this = expression.this.name if expression.this else ""
    2354            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2355
    2356        this = self.sql(expression, "this")
    2357        if this:
    2358            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2359            this = f" {this}" if unwrapped else f" ({this})"
    2360
    2361        return f"INTERVAL{this}{unit}"
    2362
    2363    def return_sql(self, expression: exp.Return) -> str:
    2364        return f"RETURN {self.sql(expression, 'this')}"
    2365
    2366    def reference_sql(self, expression: exp.Reference) -> str:
    2367        this = self.sql(expression, "this")
    2368        expressions = self.expressions(expression, flat=True)
    2369        expressions = f"({expressions})" if expressions else ""
    2370        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2371        options = f" {options}" if options else ""
    2372        return f"REFERENCES {this}{expressions}{options}"
    2373
    2374    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    2375        return self.func(expression.name, *expression.expressions)
    2376
    2377    def paren_sql(self, expression: exp.Paren) -> str:
    2378        if isinstance(expression.unnest(), exp.Select):
    2379            sql = self.wrap(expression)
    2380        else:
    2381            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2382            sql = f"({sql}{self.seg(')', sep='')}"
    2383
    2384        return self.prepend_ctes(expression, sql)
    2385
    2386    def neg_sql(self, expression: exp.Neg) -> str:
    2387        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    2388        this_sql = self.sql(expression, "this")
    2389        sep = " " if this_sql[0] == "-" else ""
    2390        return f"-{sep}{this_sql}"
    2391
    2392    def not_sql(self, expression: exp.Not) -> str:
    2393        return f"NOT {self.sql(expression, 'this')}"
    2394
    2395    def alias_sql(self, expression: exp.Alias) -> str:
    2396        alias = self.sql(expression, "alias")
    2397        alias = f" AS {alias}" if alias else ""
    2398        return f"{self.sql(expression, 'this')}{alias}"
    2399
    2400    def aliases_sql(self, expression: exp.Aliases) -> str:
    2401        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    2402
    2403    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2404        this = self.sql(expression, "this")
    2405        zone = self.sql(expression, "zone")
    2406        return f"{this} AT TIME ZONE {zone}"
    2407
    2408    def add_sql(self, expression: exp.Add) -> str:
    2409        return self.binary(expression, "+")
    2410
    2411    def and_sql(self, expression: exp.And) -> str:
    2412        return self.connector_sql(expression, "AND")
    2413
    2414    def xor_sql(self, expression: exp.Xor) -> str:
    2415        return self.connector_sql(expression, "XOR")
    2416
    2417    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2418        if not self.pretty:
    2419            return self.binary(expression, op)
    2420
    2421        sqls = tuple(
    2422            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2423            for i, e in enumerate(expression.flatten(unnest=False))
    2424        )
    2425
    2426        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2427        return f"{sep}{op} ".join(sqls)
    2428
    2429    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    2430        return self.binary(expression, "&")
    2431
    2432    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    2433        return self.binary(expression, "<<")
    2434
    2435    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    2436        return f"~{self.sql(expression, 'this')}"
    2437
    2438    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    2439        return self.binary(expression, "|")
    2440
    2441    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    2442        return self.binary(expression, ">>")
    2443
    2444    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    2445        return self.binary(expression, "^")
    2446
    2447    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2448        format_sql = self.sql(expression, "format")
    2449        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2450        to_sql = self.sql(expression, "to")
    2451        to_sql = f" {to_sql}" if to_sql else ""
    2452        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
    2453
    2454    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    2455        zone = self.sql(expression, "this")
    2456        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    2457
    2458    def collate_sql(self, expression: exp.Collate) -> str:
    2459        if self.COLLATE_IS_FUNC:
    2460            return self.function_fallback_sql(expression)
    2461        return self.binary(expression, "COLLATE")
    2462
    2463    def command_sql(self, expression: exp.Command) -> str:
    2464        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
    2465
    2466    def comment_sql(self, expression: exp.Comment) -> str:
    2467        this = self.sql(expression, "this")
    2468        kind = expression.args["kind"]
    2469        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2470        expression_sql = self.sql(expression, "expression")
    2471        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
    2472
    2473    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2474        this = self.sql(expression, "this")
    2475        delete = " DELETE" if expression.args.get("delete") else ""
    2476        recompress = self.sql(expression, "recompress")
    2477        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2478        to_disk = self.sql(expression, "to_disk")
    2479        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2480        to_volume = self.sql(expression, "to_volume")
    2481        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2482        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    2483
    2484    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2485        where = self.sql(expression, "where")
    2486        group = self.sql(expression, "group")
    2487        aggregates = self.expressions(expression, key="aggregates")
    2488        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2489
    2490        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2491            return f"TTL {self.expressions(expression, flat=True)}"
    2492
    2493        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    2494
    2495    def transaction_sql(self, expression: exp.Transaction) -> str:
    2496        return "BEGIN"
    2497
    2498    def commit_sql(self, expression: exp.Commit) -> str:
    2499        chain = expression.args.get("chain")
    2500        if chain is not None:
    2501            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2502
    2503        return f"COMMIT{chain or ''}"
    2504
    2505    def rollback_sql(self, expression: exp.Rollback) -> str:
    2506        savepoint = expression.args.get("savepoint")
    2507        savepoint = f" TO {savepoint}" if savepoint else ""
    2508        return f"ROLLBACK{savepoint}"
    2509
    2510    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2511        this = self.sql(expression, "this")
    2512
    2513        dtype = self.sql(expression, "dtype")
    2514        if dtype:
    2515            collate = self.sql(expression, "collate")
    2516            collate = f" COLLATE {collate}" if collate else ""
    2517            using = self.sql(expression, "using")
    2518            using = f" USING {using}" if using else ""
    2519            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2520
    2521        default = self.sql(expression, "default")
    2522        if default:
    2523            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2524
    2525        if not expression.args.get("drop"):
    2526            self.unsupported("Unsupported ALTER COLUMN syntax")
    2527
    2528        return f"ALTER COLUMN {this} DROP DEFAULT"
    2529
    2530    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2531        if not self.RENAME_TABLE_WITH_DB:
    2532            # Remove db from tables
    2533            expression = expression.transform(
    2534                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2535            )
    2536        this = self.sql(expression, "this")
    2537        return f"RENAME TO {this}"
    2538
    2539    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2540        actions = expression.args["actions"]
    2541
    2542        if isinstance(actions[0], exp.ColumnDef):
    2543            actions = self.add_column_sql(expression)
    2544        elif isinstance(actions[0], exp.Schema):
    2545            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2546        elif isinstance(actions[0], exp.Delete):
    2547            actions = self.expressions(expression, key="actions", flat=True)
    2548        else:
    2549            actions = self.expressions(expression, key="actions", flat=True)
    2550
    2551        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2552        only = " ONLY" if expression.args.get("only") else ""
    2553        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
    2554
    2555    def add_column_sql(self, expression: exp.AlterTable) -> str:
    2556        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    2557            return self.expressions(
    2558                expression,
    2559                key="actions",
    2560                prefix="ADD COLUMN ",
    2561            )
    2562        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
    2563
    2564    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2565        expressions = self.expressions(expression)
    2566        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2567        return f"DROP{exists}{expressions}"
    2568
    2569    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2570        this = self.sql(expression, "this")
    2571        expression_ = self.sql(expression, "expression")
    2572        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2573
    2574        enforced = expression.args.get("enforced")
    2575        if enforced is not None:
    2576            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2577
    2578        return f"{add_constraint} {expression_}"
    2579
    2580    def distinct_sql(self, expression: exp.Distinct) -> str:
    2581        this = self.expressions(expression, flat=True)
    2582        this = f" {this}" if this else ""
    2583
    2584        on = self.sql(expression, "on")
    2585        on = f" ON {on}" if on else ""
    2586        return f"DISTINCT{this}{on}"
    2587
    2588    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    2589        return f"{self.sql(expression, 'this')} IGNORE NULLS"
    2590
    2591    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    2592        return f"{self.sql(expression, 'this')} RESPECT NULLS"
    2593
    2594    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2595        return self.sql(
    2596            exp.Cast(
    2597                this=exp.Div(this=expression.this, expression=expression.expression),
    2598                to=exp.DataType(this=exp.DataType.Type.INT),
    2599            )
    2600        )
    2601
    2602    def dpipe_sql(self, expression: exp.DPipe) -> str:
    2603        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2604            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
    2605        return self.binary(expression, "||")
    2606
    2607    def div_sql(self, expression: exp.Div) -> str:
    2608        l, r = expression.left, expression.right
    2609
    2610        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    2611            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    2612
    2613        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    2614            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
    2615                *exp.DataType.FLOAT_TYPES
    2616            ):
    2617                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    2618
    2619        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    2620            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    2621                return self.sql(
    2622                    exp.cast(
    2623                        l / r,
    2624                        to=exp.DataType.Type.BIGINT,
    2625                    )
    2626                )
    2627
    2628        return self.binary(expression, "/")
    2629
    2630    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    2631        return self.binary(expression, "OVERLAPS")
    2632
    2633    def distance_sql(self, expression: exp.Distance) -> str:
    2634        return self.binary(expression, "<->")
    2635
    2636    def dot_sql(self, expression: exp.Dot) -> str:
    2637        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    2638
    2639    def eq_sql(self, expression: exp.EQ) -> str:
    2640        return self.binary(expression, "=")
    2641
    2642    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    2643        return self.binary(expression, ":=")
    2644
    2645    def escape_sql(self, expression: exp.Escape) -> str:
    2646        return self.binary(expression, "ESCAPE")
    2647
    2648    def glob_sql(self, expression: exp.Glob) -> str:
    2649        return self.binary(expression, "GLOB")
    2650
    2651    def gt_sql(self, expression: exp.GT) -> str:
    2652        return self.binary(expression, ">")
    2653
    2654    def gte_sql(self, expression: exp.GTE) -> str:
    2655        return self.binary(expression, ">=")
    2656
    2657    def ilike_sql(self, expression: exp.ILike) -> str:
    2658        return self.binary(expression, "ILIKE")
    2659
    2660    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    2661        return self.binary(expression, "ILIKE ANY")
    2662
    2663    def is_sql(self, expression: exp.Is) -> str:
    2664        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    2665            return self.sql(
    2666                expression.this if expression.expression.this else exp.not_(expression.this)
    2667            )
    2668        return self.binary(expression, "IS")
    2669
    2670    def like_sql(self, expression: exp.Like) -> str:
    2671        return self.binary(expression, "LIKE")
    2672
    2673    def likeany_sql(self, expression: exp.LikeAny) -> str:
    2674        return self.binary(expression, "LIKE ANY")
    2675
    2676    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    2677        return self.binary(expression, "SIMILAR TO")
    2678
    2679    def lt_sql(self, expression: exp.LT) -> str:
    2680        return self.binary(expression, "<")
    2681
    2682    def lte_sql(self, expression: exp.LTE) -> str:
    2683        return self.binary(expression, "<=")
    2684
    2685    def mod_sql(self, expression: exp.Mod) -> str:
    2686        return self.binary(expression, "%")
    2687
    2688    def mul_sql(self, expression: exp.Mul) -> str:
    2689        return self.binary(expression, "*")
    2690
    2691    def neq_sql(self, expression: exp.NEQ) -> str:
    2692        return self.binary(expression, "<>")
    2693
    2694    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    2695        return self.binary(expression, "IS NOT DISTINCT FROM")
    2696
    2697    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    2698        return self.binary(expression, "IS DISTINCT FROM")
    2699
    2700    def or_sql(self, expression: exp.Or) -> str:
    2701        return self.connector_sql(expression, "OR")
    2702
    2703    def slice_sql(self, expression: exp.Slice) -> str:
    2704        return self.binary(expression, ":")
    2705
    2706    def sub_sql(self, expression: exp.Sub) -> str:
    2707        return self.binary(expression, "-")
    2708
    2709    def trycast_sql(self, expression: exp.TryCast) -> str:
    2710        return self.cast_sql(expression, safe_prefix="TRY_")
    2711
    2712    def log_sql(self, expression: exp.Log) -> str:
    2713        this = expression.this
    2714        expr = expression.expression
    2715
    2716        if not self.dialect.LOG_BASE_FIRST:
    2717            this, expr = expr, this
    2718
    2719        return self.func("LOG", this, expr)
    2720
    2721    def use_sql(self, expression: exp.Use) -> str:
    2722        kind = self.sql(expression, "kind")
    2723        kind = f" {kind}" if kind else ""
    2724        this = self.sql(expression, "this")
    2725        this = f" {this}" if this else ""
    2726        return f"USE{kind}{this}"
    2727
    2728    def binary(self, expression: exp.Binary, op: str) -> str:
    2729        op = self.maybe_comment(op, comments=expression.comments)
    2730        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
    2731
    2732    def function_fallback_sql(self, expression: exp.Func) -> str:
    2733        args = []
    2734
    2735        for key in expression.arg_types:
    2736            arg_value = expression.args.get(key)
    2737
    2738            if isinstance(arg_value, list):
    2739                for value in arg_value:
    2740                    args.append(value)
    2741            elif arg_value is not None:
    2742                args.append(arg_value)
    2743
    2744        if self.normalize_functions:
    2745            name = expression.sql_name()
    2746        else:
    2747            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    2748
    2749        return self.func(name, *args)
    2750
    2751    def func(
    2752        self,
    2753        name: str,
    2754        *args: t.Optional[exp.Expression | str],
    2755        prefix: str = "(",
    2756        suffix: str = ")",
    2757    ) -> str:
    2758        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
    2759
    2760    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    2761        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    2762        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
    2763            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    2764        return ", ".join(arg_sqls)
    2765
    2766    def text_width(self, args: t.Iterable) -> int:
    2767        return sum(len(arg) for arg in args)
    2768
    2769    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2770        return format_time(
    2771            self.sql(expression, "format"),
    2772            self.dialect.INVERSE_TIME_MAPPING,
    2773            self.dialect.INVERSE_TIME_TRIE,
    2774        )
    2775
    2776    def expressions(
    2777        self,
    2778        expression: t.Optional[exp.Expression] = None,
    2779        key: t.Optional[str] = None,
    2780        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    2781        flat: bool = False,
    2782        indent: bool = True,
    2783        skip_first: bool = False,
    2784        sep: str = ", ",
    2785        prefix: str = "",
    2786    ) -> str:
    2787        expressions = expression.args.get(key or "expressions") if expression else sqls
    2788
    2789        if not expressions:
    2790            return ""
    2791
    2792        if flat:
    2793            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    2794
    2795        num_sqls = len(expressions)
    2796
    2797        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2798        pad = " " * self.pad
    2799        stripped_sep = sep.strip()
    2800
    2801        result_sqls = []
    2802        for i, e in enumerate(expressions):
    2803            sql = self.sql(e, comment=False)
    2804            if not sql:
    2805                continue
    2806
    2807            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2808
    2809            if self.pretty:
    2810                if self.leading_comma:
    2811                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2812                else:
    2813                    result_sqls.append(
    2814                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2815                    )
    2816            else:
    2817                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2818
    2819        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2820        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
    2821
    2822    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2823        flat = flat or isinstance(expression.parent, exp.Properties)
    2824        expressions_sql = self.expressions(expression, flat=flat)
    2825        if flat:
    2826            return f"{op} {expressions_sql}"
    2827        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    2828
    2829    def naked_property(self, expression: exp.Property) -> str:
    2830        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2831        if not property_name:
    2832            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2833        return f"{property_name} {self.sql(expression, 'this')}"
    2834
    2835    def set_operation(self, expression: exp.Union, op: str) -> str:
    2836        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
    2837        op = self.seg(op)
    2838        return self.query_modifiers(
    2839            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2840        )
    2841
    2842    def tag_sql(self, expression: exp.Tag) -> str:
    2843        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    2844
    2845    def token_sql(self, token_type: TokenType) -> str:
    2846        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    2847
    2848    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2849        this = self.sql(expression, "this")
    2850        expressions = self.no_identify(self.expressions, expression)
    2851        expressions = (
    2852            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2853        )
    2854        return f"{this}{expressions}"
    2855
    2856    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2857        this = self.sql(expression, "this")
    2858        expressions = self.expressions(expression, flat=True)
    2859        return f"{this}({expressions})"
    2860
    2861    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    2862        return self.binary(expression, "=>")
    2863
    2864    def when_sql(self, expression: exp.When) -> str:
    2865        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2866        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2867        condition = self.sql(expression, "condition")
    2868        condition = f" AND {condition}" if condition else ""
    2869
    2870        then_expression = expression.args.get("then")
    2871        if isinstance(then_expression, exp.Insert):
    2872            then = f"INSERT {self.sql(then_expression, 'this')}"
    2873            if "expression" in then_expression.args:
    2874                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2875        elif isinstance(then_expression, exp.Update):
    2876            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2877                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2878            else:
    2879                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2880        else:
    2881            then = self.sql(then_expression)
    2882        return f"WHEN {matched}{source}{condition} THEN {then}"
    2883
    2884    def merge_sql(self, expression: exp.Merge) -> str:
    2885        table = expression.this
    2886        table_alias = ""
    2887
    2888        hints = table.args.get("hints")
    2889        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    2890            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    2891            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    2892
    2893        this = self.sql(table)
    2894        using = f"USING {self.sql(expression, 'using')}"
    2895        on = f"ON {self.sql(expression, 'on')}"
    2896        expressions = self.expressions(expression, sep=" ")
    2897
    2898        return self.prepend_ctes(
    2899            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
    2900        )
    2901
    2902    def tochar_sql(self, expression: exp.ToChar) -> str:
    2903        if expression.args.get("format"):
    2904            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2905
    2906        return self.sql(exp.cast(expression.this, "text"))
    2907
    2908    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2909        this = self.sql(expression, "this")
    2910        kind = self.sql(expression, "kind")
    2911        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2912        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2913        return f"{this}({kind}{args})"
    2914
    2915    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2916        this = self.sql(expression, "this")
    2917        max = self.sql(expression, "max")
    2918        min = self.sql(expression, "min")
    2919        return f"{this}(MIN {min} MAX {max})"
    2920
    2921    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    2922        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    2923
    2924    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    2925        return ""
    2926
    2927    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    2928        expressions = self.expressions(expression, key="expressions", flat=True)
    2929        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    2930        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    2931        buckets = self.sql(expression, "buckets")
    2932        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
    2933
    2934    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    2935        this = self.sql(expression, "this")
    2936        having = self.sql(expression, "having")
    2937
    2938        if having:
    2939            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    2940
    2941        return self.func("ANY_VALUE", this)
    2942
    2943    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    2944        transform = self.func("TRANSFORM", *expression.expressions)
    2945        row_format_before = self.sql(expression, "row_format_before")
    2946        row_format_before = f" {row_format_before}" if row_format_before else ""
    2947        record_writer = self.sql(expression, "record_writer")
    2948        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    2949        using = f" USING {self.sql(expression, 'command_script')}"
    2950        schema = self.sql(expression, "schema")
    2951        schema = f" AS {schema}" if schema else ""
    2952        row_format_after = self.sql(expression, "row_format_after")
    2953        row_format_after = f" {row_format_after}" if row_format_after else ""
    2954        record_reader = self.sql(expression, "record_reader")
    2955        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    2956        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
    2957
    2958    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    2959        key_block_size = self.sql(expression, "key_block_size")
    2960        if key_block_size:
    2961            return f"KEY_BLOCK_SIZE = {key_block_size}"
    2962
    2963        using = self.sql(expression, "using")
    2964        if using:
    2965            return f"USING {using}"
    2966
    2967        parser = self.sql(expression, "parser")
    2968        if parser:
    2969            return f"WITH PARSER {parser}"
    2970
    2971        comment = self.sql(expression, "comment")
    2972        if comment:
    2973            return f"COMMENT {comment}"
    2974
    2975        visible = expression.args.get("visible")
    2976        if visible is not None:
    2977            return "VISIBLE" if visible else "INVISIBLE"
    2978
    2979        engine_attr = self.sql(expression, "engine_attr")
    2980        if engine_attr:
    2981            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    2982
    2983        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    2984        if secondary_engine_attr:
    2985            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    2986
    2987        self.unsupported("Unsupported index constraint option.")
    2988        return ""
    2989
    2990    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    2991        kind = self.sql(expression, "kind")
    2992        kind = f"{kind} INDEX" if kind else "INDEX"
    2993        this = self.sql(expression, "this")
    2994        this = f" {this}" if this else ""
    2995        index_type = self.sql(expression, "index_type")
    2996        index_type = f" USING {index_type}" if index_type else ""
    2997        schema = self.sql(expression, "schema")
    2998        schema = f" {schema}" if schema else ""
    2999        options = self.expressions(expression, key="options", sep=" ")
    3000        options = f" {options}" if options else ""
    3001        return f"{kind}{this}{index_type}{schema}{options}"
    3002
    3003    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3004        if self.NVL2_SUPPORTED:
    3005            return self.function_fallback_sql(expression)
    3006
    3007        case = exp.Case().when(
    3008            expression.this.is_(exp.null()).not_(copy=False),
    3009            expression.args["true"],
    3010            copy=False,
    3011        )
    3012        else_cond = expression.args.get("false")
    3013        if else_cond:
    3014            case.else_(else_cond, copy=False)
    3015
    3016        return self.sql(case)
    3017
    3018    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3019        this = self.sql(expression, "this")
    3020        expr = self.sql(expression, "expression")
    3021        iterator = self.sql(expression, "iterator")
    3022        condition = self.sql(expression, "condition")
    3023        condition = f" IF {condition}" if condition else ""
    3024        return f"{this} FOR {expr} IN {iterator}{condition}"
    3025
    3026    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    3027        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
    3028
    3029    def opclass_sql(self, expression: exp.Opclass) -> str:
    3030        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    3031
    3032    def predict_sql(self, expression: exp.Predict) -> str:
    3033        model = self.sql(expression, "this")
    3034        model = f"MODEL {model}"
    3035        table = self.sql(expression, "expression")
    3036        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3037        parameters = self.sql(expression, "params_struct")
    3038        return self.func("PREDICT", model, table, parameters or None)
    3039
    3040    def forin_sql(self, expression: exp.ForIn) -> str:
    3041        this = self.sql(expression, "this")
    3042        expression_sql = self.sql(expression, "expression")
    3043        return f"FOR {this} DO {expression_sql}"
    3044
    3045    def refresh_sql(self, expression: exp.Refresh) -> str:
    3046        this = self.sql(expression, "this")
    3047        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3048        return f"REFRESH {table}{this}"
    3049
    3050    def operator_sql(self, expression: exp.Operator) -> str:
    3051        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
    3052
    3053    def _simplify_unless_literal(self, expression: E) -> E:
    3054        if not isinstance(expression, exp.Literal):
    3055            from sqlglot.optimizer.simplify import simplify
    3056
    3057            expression = simplify(expression)
    3058
    3059        return expression
    3060
    3061    def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]:
    3062        return [
    3063            exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string(""))
    3064            for value in values
    3065            if value
    3066        ]

logger = <Logger sqlglot (WARNING)>
class Generator: View Source

      22class Generator:
      23    """
      24    Generator converts a given syntax tree to the corresponding SQL string.
      25
      26    Args:
      27        pretty: Whether or not to format the produced SQL string.
      28            Default: False.
      29        identify: Determines when an identifier should be quoted. Possible values are:
      30            False (default): Never quote, except in cases where it's mandatory by the dialect.
      31            True or 'always': Always quote.
      32            'safe': Only quote identifiers that are case insensitive.
      33        normalize: Whether or not to normalize identifiers to lowercase.
      34            Default: False.
      35        pad: Determines the pad size in a formatted string.
      36            Default: 2.
      37        indent: Determines the indentation size in a formatted string.
      38            Default: 2.
      39        normalize_functions: Whether or not to normalize all function names. Possible values are:
      40            "upper" or True (default): Convert names to uppercase.
      41            "lower": Convert names to lowercase.
      42            False: Disables function name normalization.
      43        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
      44            Default ErrorLevel.WARN.
      45        max_unsupported: 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: Determines whether or not the comma is leading or trailing in select expressions.
      49            This is only relevant when generating in pretty mode.
      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, exp.Literal.string(e.text("unit"))
      62        ),
      63        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
      64        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
      65        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
      66        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
      67        exp.ClusteredColumnConstraint: lambda self, e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
      68        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
      69        exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS",
      70        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
      71        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
      72        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
      73        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
      74        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
      75        exp.ExternalProperty: lambda self, e: "EXTERNAL",
      76        exp.HeapProperty: lambda self, e: "HEAP",
      77        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
      78        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
      79        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
      80        exp.LanguageProperty: lambda self, e: self.naked_property(e),
      81        exp.LocationProperty: lambda self, e: self.naked_property(e),
      82        exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
      83        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
      84        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
      85        exp.NonClusteredColumnConstraint: lambda self, e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
      86        exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION",
      87        exp.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
      88        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
      89        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
      90        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
      91        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
      92        exp.RemoteWithConnectionModelProperty: lambda self, e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
      93        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
      94        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
      95        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
      96        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
      97        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
      98        exp.StabilityProperty: lambda self, e: e.name,
      99        exp.TemporaryProperty: lambda self, e: f"TEMPORARY",
     100        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
     101        exp.TransientProperty: lambda self, e: "TRANSIENT",
     102        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
     103        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     104        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
     105        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
     106        exp.VolatileProperty: lambda self, e: "VOLATILE",
     107        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
     108    }
     109
     110    # Whether or not null ordering is supported in order by
     111    NULL_ORDERING_SUPPORTED = True
     112
     113    # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     114    LOCKING_READS_SUPPORTED = False
     115
     116    # Always do union distinct or union all
     117    EXPLICIT_UNION = False
     118
     119    # Wrap derived values in parens, usually standard but spark doesn't support it
     120    WRAP_DERIVED_VALUES = True
     121
     122    # Whether or not create function uses an AS before the RETURN
     123    CREATE_FUNCTION_RETURN_AS = True
     124
     125    # Whether or not MERGE ... WHEN MATCHED BY SOURCE is allowed
     126    MATCHED_BY_SOURCE = True
     127
     128    # Whether or not the INTERVAL expression works only with values like '1 day'
     129    SINGLE_STRING_INTERVAL = False
     130
     131    # Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     132    INTERVAL_ALLOWS_PLURAL_FORM = True
     133
     134    # Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI
     135    TABLESAMPLE_WITH_METHOD = True
     136
     137    # Whether or not to treat the number in TABLESAMPLE (50) as a percentage
     138    TABLESAMPLE_SIZE_IS_PERCENT = False
     139
     140    # Whether or not limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     141    LIMIT_FETCH = "ALL"
     142
     143    # Whether or not limit and fetch allows expresions or just limits
     144    LIMIT_ONLY_LITERALS = False
     145
     146    # Whether or not a table is allowed to be renamed with a db
     147    RENAME_TABLE_WITH_DB = True
     148
     149    # The separator for grouping sets and rollups
     150    GROUPINGS_SEP = ","
     151
     152    # The string used for creating an index on a table
     153    INDEX_ON = "ON"
     154
     155    # Whether or not join hints should be generated
     156    JOIN_HINTS = True
     157
     158    # Whether or not table hints should be generated
     159    TABLE_HINTS = True
     160
     161    # Whether or not query hints should be generated
     162    QUERY_HINTS = True
     163
     164    # What kind of separator to use for query hints
     165    QUERY_HINT_SEP = ", "
     166
     167    # Whether or not comparing against booleans (e.g. x IS TRUE) is supported
     168    IS_BOOL_ALLOWED = True
     169
     170    # Whether or not to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
     171    DUPLICATE_KEY_UPDATE_WITH_SET = True
     172
     173    # Whether or not to generate the limit as TOP <value> instead of LIMIT <value>
     174    LIMIT_IS_TOP = False
     175
     176    # Whether or not to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
     177    RETURNING_END = True
     178
     179    # Whether or not to generate the (+) suffix for columns used in old-style join conditions
     180    COLUMN_JOIN_MARKS_SUPPORTED = False
     181
     182    # Whether or not to generate an unquoted value for EXTRACT's date part argument
     183    EXTRACT_ALLOWS_QUOTES = True
     184
     185    # Whether or not TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
     186    TZ_TO_WITH_TIME_ZONE = False
     187
     188    # Whether or not the NVL2 function is supported
     189    NVL2_SUPPORTED = True
     190
     191    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
     192    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
     193
     194    # Whether or not VALUES statements can be used as derived tables.
     195    # MySQL 5 and Redshift do not allow this, so when False, it will convert
     196    # SELECT * VALUES into SELECT UNION
     197    VALUES_AS_TABLE = True
     198
     199    # Whether or not the word COLUMN is included when adding a column with ALTER TABLE
     200    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
     201
     202    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     203    UNNEST_WITH_ORDINALITY = True
     204
     205    # Whether or not FILTER (WHERE cond) can be used for conditional aggregation
     206    AGGREGATE_FILTER_SUPPORTED = True
     207
     208    # Whether or not JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
     209    SEMI_ANTI_JOIN_WITH_SIDE = True
     210
     211    # Whether or not to include the type of a computed column in the CREATE DDL
     212    COMPUTED_COLUMN_WITH_TYPE = True
     213
     214    # Whether or not CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
     215    SUPPORTS_TABLE_COPY = True
     216
     217    # Whether or not parentheses are required around the table sample's expression
     218    TABLESAMPLE_REQUIRES_PARENS = True
     219
     220    # Whether or not COLLATE is a function instead of a binary operator
     221    COLLATE_IS_FUNC = False
     222
     223    # Whether or not data types support additional specifiers like e.g. CHAR or BYTE (oracle)
     224    DATA_TYPE_SPECIFIERS_ALLOWED = False
     225
     226    # Whether or not conditions require booleans WHERE x = 0 vs WHERE x
     227    ENSURE_BOOLS = False
     228
     229    # Whether or not the "RECURSIVE" keyword is required when defining recursive CTEs
     230    CTE_RECURSIVE_KEYWORD_REQUIRED = True
     231
     232    # Whether or not CONCAT requires >1 arguments
     233    SUPPORTS_SINGLE_ARG_CONCAT = True
     234
     235    TYPE_MAPPING = {
     236        exp.DataType.Type.NCHAR: "CHAR",
     237        exp.DataType.Type.NVARCHAR: "VARCHAR",
     238        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     239        exp.DataType.Type.LONGTEXT: "TEXT",
     240        exp.DataType.Type.TINYTEXT: "TEXT",
     241        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     242        exp.DataType.Type.LONGBLOB: "BLOB",
     243        exp.DataType.Type.TINYBLOB: "BLOB",
     244        exp.DataType.Type.INET: "INET",
     245    }
     246
     247    STAR_MAPPING = {
     248        "except": "EXCEPT",
     249        "replace": "REPLACE",
     250    }
     251
     252    TIME_PART_SINGULARS = {
     253        "microseconds": "microsecond",
     254        "seconds": "second",
     255        "minutes": "minute",
     256        "hours": "hour",
     257        "days": "day",
     258        "weeks": "week",
     259        "months": "month",
     260        "quarters": "quarter",
     261        "years": "year",
     262    }
     263
     264    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     265
     266    STRUCT_DELIMITER = ("<", ">")
     267
     268    PARAMETER_TOKEN = "@"
     269
     270    PROPERTIES_LOCATION = {
     271        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     272        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     273        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     274        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     275        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     276        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     277        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
     278        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     279        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
     280        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     281        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     282        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     283        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     284        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     285        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     286        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     287        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     288        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     289        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     290        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     291        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     292        exp.HeapProperty: exp.Properties.Location.POST_WITH,
     293        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
     294        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     295        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     296        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     297        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     298        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     299        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     300        exp.LogProperty: exp.Properties.Location.POST_NAME,
     301        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     302        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     303        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     304        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
     305        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     306        exp.Order: exp.Properties.Location.POST_SCHEMA,
     307        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
     308        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     309        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
     310        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     311        exp.Property: exp.Properties.Location.POST_WITH,
     312        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
     313        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     314        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     315        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     316        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     317        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
     318        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     319        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     320        exp.Set: exp.Properties.Location.POST_SCHEMA,
     321        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     322        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     323        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     324        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     325        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     326        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     327        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
     328        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     329        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
     330        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     331        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     332        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     333        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     334        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
     335    }
     336
     337    # Keywords that can't be used as unquoted identifier names
     338    RESERVED_KEYWORDS: t.Set[str] = set()
     339
     340    # Expressions whose comments are separated from them for better formatting
     341    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     342        exp.Create,
     343        exp.Delete,
     344        exp.Drop,
     345        exp.From,
     346        exp.Insert,
     347        exp.Join,
     348        exp.Select,
     349        exp.Update,
     350        exp.Where,
     351        exp.With,
     352    )
     353
     354    # Expressions that should not have their comments generated in maybe_comment
     355    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     356        exp.Binary,
     357        exp.Union,
     358    )
     359
     360    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
     361    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
     362        exp.Column,
     363        exp.Literal,
     364        exp.Neg,
     365        exp.Paren,
     366    )
     367
     368    # Expressions that need to have all CTEs under them bubbled up to them
     369    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
     370
     371    KEY_VALUE_DEFINITONS = (exp.Bracket, exp.EQ, exp.PropertyEQ, exp.Slice)
     372
     373    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     374
     375    __slots__ = (
     376        "pretty",
     377        "identify",
     378        "normalize",
     379        "pad",
     380        "_indent",
     381        "normalize_functions",
     382        "unsupported_level",
     383        "max_unsupported",
     384        "leading_comma",
     385        "max_text_width",
     386        "comments",
     387        "dialect",
     388        "unsupported_messages",
     389        "_escaped_quote_end",
     390        "_escaped_identifier_end",
     391    )
     392
     393    def __init__(
     394        self,
     395        pretty: t.Optional[bool] = None,
     396        identify: str | bool = False,
     397        normalize: bool = False,
     398        pad: int = 2,
     399        indent: int = 2,
     400        normalize_functions: t.Optional[str | bool] = None,
     401        unsupported_level: ErrorLevel = ErrorLevel.WARN,
     402        max_unsupported: int = 3,
     403        leading_comma: bool = False,
     404        max_text_width: int = 80,
     405        comments: bool = True,
     406        dialect: DialectType = None,
     407    ):
     408        import sqlglot
     409        from sqlglot.dialects import Dialect
     410
     411        self.pretty = pretty if pretty is not None else sqlglot.pretty
     412        self.identify = identify
     413        self.normalize = normalize
     414        self.pad = pad
     415        self._indent = indent
     416        self.unsupported_level = unsupported_level
     417        self.max_unsupported = max_unsupported
     418        self.leading_comma = leading_comma
     419        self.max_text_width = max_text_width
     420        self.comments = comments
     421        self.dialect = Dialect.get_or_raise(dialect)
     422
     423        # This is both a Dialect property and a Generator argument, so we prioritize the latter
     424        self.normalize_functions = (
     425            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
     426        )
     427
     428        self.unsupported_messages: t.List[str] = []
     429        self._escaped_quote_end: str = (
     430            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
     431        )
     432        self._escaped_identifier_end: str = (
     433            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
     434        )
     435
     436    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
     437        """
     438        Generates the SQL string corresponding to the given syntax tree.
     439
     440        Args:
     441            expression: The syntax tree.
     442            copy: Whether or not to copy the expression. The generator performs mutations so
     443                it is safer to copy.
     444
     445        Returns:
     446            The SQL string corresponding to `expression`.
     447        """
     448        if copy:
     449            expression = expression.copy()
     450
     451        expression = self.preprocess(expression)
     452
     453        self.unsupported_messages = []
     454        sql = self.sql(expression).strip()
     455
     456        if self.pretty:
     457            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     458
     459        if self.unsupported_level == ErrorLevel.IGNORE:
     460            return sql
     461
     462        if self.unsupported_level == ErrorLevel.WARN:
     463            for msg in self.unsupported_messages:
     464                logger.warning(msg)
     465        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     466            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     467
     468        return sql
     469
     470    def preprocess(self, expression: exp.Expression) -> exp.Expression:
     471        """Apply generic preprocessing transformations to a given expression."""
     472        if (
     473            not expression.parent
     474            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
     475            and any(node.parent is not expression for node in expression.find_all(exp.With))
     476        ):
     477            from sqlglot.transforms import move_ctes_to_top_level
     478
     479            expression = move_ctes_to_top_level(expression)
     480
     481        if self.ENSURE_BOOLS:
     482            from sqlglot.transforms import ensure_bools
     483
     484            expression = ensure_bools(expression)
     485
     486        return expression
     487
     488    def unsupported(self, message: str) -> None:
     489        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     490            raise UnsupportedError(message)
     491        self.unsupported_messages.append(message)
     492
     493    def sep(self, sep: str = " ") -> str:
     494        return f"{sep.strip()}\n" if self.pretty else sep
     495
     496    def seg(self, sql: str, sep: str = " ") -> str:
     497        return f"{self.sep(sep)}{sql}"
     498
     499    def pad_comment(self, comment: str) -> str:
     500        comment = " " + comment if comment[0].strip() else comment
     501        comment = comment + " " if comment[-1].strip() else comment
     502        return comment
     503
     504    def maybe_comment(
     505        self,
     506        sql: str,
     507        expression: t.Optional[exp.Expression] = None,
     508        comments: t.Optional[t.List[str]] = None,
     509    ) -> str:
     510        comments = (
     511            ((expression and expression.comments) if comments is None else comments)  # type: ignore
     512            if self.comments
     513            else None
     514        )
     515
     516        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
     517            return sql
     518
     519        comments_sql = " ".join(
     520            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     521        )
     522
     523        if not comments_sql:
     524            return sql
     525
     526        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     527            return (
     528                f"{self.sep()}{comments_sql}{sql}"
     529                if sql[0].isspace()
     530                else f"{comments_sql}{self.sep()}{sql}"
     531            )
     532
     533        return f"{sql} {comments_sql}"
     534
     535    def wrap(self, expression: exp.Expression | str) -> str:
     536        this_sql = self.indent(
     537            self.sql(expression)
     538            if isinstance(expression, (exp.Select, exp.Union))
     539            else self.sql(expression, "this"),
     540            level=1,
     541            pad=0,
     542        )
     543        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     544
     545    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     546        original = self.identify
     547        self.identify = False
     548        result = func(*args, **kwargs)
     549        self.identify = original
     550        return result
     551
     552    def normalize_func(self, name: str) -> str:
     553        if self.normalize_functions == "upper" or self.normalize_functions is True:
     554            return name.upper()
     555        if self.normalize_functions == "lower":
     556            return name.lower()
     557        return name
     558
     559    def indent(
     560        self,
     561        sql: str,
     562        level: int = 0,
     563        pad: t.Optional[int] = None,
     564        skip_first: bool = False,
     565        skip_last: bool = False,
     566    ) -> str:
     567        if not self.pretty:
     568            return sql
     569
     570        pad = self.pad if pad is None else pad
     571        lines = sql.split("\n")
     572
     573        return "\n".join(
     574            line
     575            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     576            else f"{' ' * (level * self._indent + pad)}{line}"
     577            for i, line in enumerate(lines)
     578        )
     579
     580    def sql(
     581        self,
     582        expression: t.Optional[str | exp.Expression],
     583        key: t.Optional[str] = None,
     584        comment: bool = True,
     585    ) -> str:
     586        if not expression:
     587            return ""
     588
     589        if isinstance(expression, str):
     590            return expression
     591
     592        if key:
     593            value = expression.args.get(key)
     594            if value:
     595                return self.sql(value)
     596            return ""
     597
     598        transform = self.TRANSFORMS.get(expression.__class__)
     599
     600        if callable(transform):
     601            sql = transform(self, expression)
     602        elif transform:
     603            sql = transform
     604        elif isinstance(expression, exp.Expression):
     605            exp_handler_name = f"{expression.key}_sql"
     606
     607            if hasattr(self, exp_handler_name):
     608                sql = getattr(self, exp_handler_name)(expression)
     609            elif isinstance(expression, exp.Func):
     610                sql = self.function_fallback_sql(expression)
     611            elif isinstance(expression, exp.Property):
     612                sql = self.property_sql(expression)
     613            else:
     614                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     615        else:
     616            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     617
     618        return self.maybe_comment(sql, expression) if self.comments and comment else sql
     619
     620    def uncache_sql(self, expression: exp.Uncache) -> str:
     621        table = self.sql(expression, "this")
     622        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     623        return f"UNCACHE TABLE{exists_sql} {table}"
     624
     625    def cache_sql(self, expression: exp.Cache) -> str:
     626        lazy = " LAZY" if expression.args.get("lazy") else ""
     627        table = self.sql(expression, "this")
     628        options = expression.args.get("options")
     629        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     630        sql = self.sql(expression, "expression")
     631        sql = f" AS{self.sep()}{sql}" if sql else ""
     632        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     633        return self.prepend_ctes(expression, sql)
     634
     635    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     636        if isinstance(expression.parent, exp.Cast):
     637            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     638        default = "DEFAULT " if expression.args.get("default") else ""
     639        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     640
     641    def column_sql(self, expression: exp.Column) -> str:
     642        join_mark = " (+)" if expression.args.get("join_mark") else ""
     643
     644        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
     645            join_mark = ""
     646            self.unsupported("Outer join syntax using the (+) operator is not supported.")
     647
     648        column = ".".join(
     649            self.sql(part)
     650            for part in (
     651                expression.args.get("catalog"),
     652                expression.args.get("db"),
     653                expression.args.get("table"),
     654                expression.args.get("this"),
     655            )
     656            if part
     657        )
     658
     659        return f"{column}{join_mark}"
     660
     661    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     662        this = self.sql(expression, "this")
     663        this = f" {this}" if this else ""
     664        position = self.sql(expression, "position")
     665        return f"{position}{this}"
     666
     667    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     668        column = self.sql(expression, "this")
     669        kind = self.sql(expression, "kind")
     670        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     671        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     672        kind = f"{sep}{kind}" if kind else ""
     673        constraints = f" {constraints}" if constraints else ""
     674        position = self.sql(expression, "position")
     675        position = f" {position}" if position else ""
     676
     677        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
     678            kind = ""
     679
     680        return f"{exists}{column}{kind}{constraints}{position}"
     681
     682    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     683        this = self.sql(expression, "this")
     684        kind_sql = self.sql(expression, "kind").strip()
     685        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     686
     687    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     688        this = self.sql(expression, "this")
     689        if expression.args.get("not_null"):
     690            persisted = " PERSISTED NOT NULL"
     691        elif expression.args.get("persisted"):
     692            persisted = " PERSISTED"
     693        else:
     694            persisted = ""
     695        return f"AS {this}{persisted}"
     696
     697    def autoincrementcolumnconstraint_sql(self, _) -> str:
     698        return self.token_sql(TokenType.AUTO_INCREMENT)
     699
     700    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     701        if isinstance(expression.this, list):
     702            this = self.wrap(self.expressions(expression, key="this", flat=True))
     703        else:
     704            this = self.sql(expression, "this")
     705
     706        return f"COMPRESS {this}"
     707
     708    def generatedasidentitycolumnconstraint_sql(
     709        self, expression: exp.GeneratedAsIdentityColumnConstraint
     710    ) -> str:
     711        this = ""
     712        if expression.this is not None:
     713            on_null = " ON NULL" if expression.args.get("on_null") else ""
     714            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
     715
     716        start = expression.args.get("start")
     717        start = f"START WITH {start}" if start else ""
     718        increment = expression.args.get("increment")
     719        increment = f" INCREMENT BY {increment}" if increment else ""
     720        minvalue = expression.args.get("minvalue")
     721        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
     722        maxvalue = expression.args.get("maxvalue")
     723        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
     724        cycle = expression.args.get("cycle")
     725        cycle_sql = ""
     726
     727        if cycle is not None:
     728            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
     729            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
     730
     731        sequence_opts = ""
     732        if start or increment or cycle_sql:
     733            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
     734            sequence_opts = f" ({sequence_opts.strip()})"
     735
     736        expr = self.sql(expression, "expression")
     737        expr = f"({expr})" if expr else "IDENTITY"
     738
     739        return f"GENERATED{this} AS {expr}{sequence_opts}"
     740
     741    def generatedasrowcolumnconstraint_sql(
     742        self, expression: exp.GeneratedAsRowColumnConstraint
     743    ) -> str:
     744        start = "START" if expression.args["start"] else "END"
     745        hidden = " HIDDEN" if expression.args.get("hidden") else ""
     746        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
     747
     748    def periodforsystemtimeconstraint_sql(
     749        self, expression: exp.PeriodForSystemTimeConstraint
     750    ) -> str:
     751        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
     752
     753    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
     754        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
     755
     756    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
     757        return f"AS {self.sql(expression, 'this')}"
     758
     759    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
     760        desc = expression.args.get("desc")
     761        if desc is not None:
     762            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
     763        return f"PRIMARY KEY"
     764
     765    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
     766        this = self.sql(expression, "this")
     767        this = f" {this}" if this else ""
     768        index_type = expression.args.get("index_type")
     769        index_type = f" USING {index_type}" if index_type else ""
     770        return f"UNIQUE{this}{index_type}"
     771
     772    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
     773        return self.sql(expression, "this")
     774
     775    def create_sql(self, expression: exp.Create) -> str:
     776        kind = self.sql(expression, "kind").upper()
     777        properties = expression.args.get("properties")
     778        properties_locs = self.locate_properties(properties) if properties else defaultdict()
     779
     780        this = self.createable_sql(expression, properties_locs)
     781
     782        properties_sql = ""
     783        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
     784            exp.Properties.Location.POST_WITH
     785        ):
     786            properties_sql = self.sql(
     787                exp.Properties(
     788                    expressions=[
     789                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
     790                        *properties_locs[exp.Properties.Location.POST_WITH],
     791                    ]
     792                )
     793            )
     794
     795        begin = " BEGIN" if expression.args.get("begin") else ""
     796        end = " END" if expression.args.get("end") else ""
     797
     798        expression_sql = self.sql(expression, "expression")
     799        if expression_sql:
     800            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
     801
     802            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
     803                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
     804                    postalias_props_sql = self.properties(
     805                        exp.Properties(
     806                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
     807                        ),
     808                        wrapped=False,
     809                    )
     810                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
     811                else:
     812                    expression_sql = f" AS{expression_sql}"
     813
     814        postindex_props_sql = ""
     815        if properties_locs.get(exp.Properties.Location.POST_INDEX):
     816            postindex_props_sql = self.properties(
     817                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
     818                wrapped=False,
     819                prefix=" ",
     820            )
     821
     822        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
     823        indexes = f" {indexes}" if indexes else ""
     824        index_sql = indexes + postindex_props_sql
     825
     826        replace = " OR REPLACE" if expression.args.get("replace") else ""
     827        unique = " UNIQUE" if expression.args.get("unique") else ""
     828
     829        postcreate_props_sql = ""
     830        if properties_locs.get(exp.Properties.Location.POST_CREATE):
     831            postcreate_props_sql = self.properties(
     832                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
     833                sep=" ",
     834                prefix=" ",
     835                wrapped=False,
     836            )
     837
     838        modifiers = "".join((replace, unique, postcreate_props_sql))
     839
     840        postexpression_props_sql = ""
     841        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
     842            postexpression_props_sql = self.properties(
     843                exp.Properties(
     844                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
     845                ),
     846                sep=" ",
     847                prefix=" ",
     848                wrapped=False,
     849            )
     850
     851        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
     852        no_schema_binding = (
     853            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
     854        )
     855
     856        clone = self.sql(expression, "clone")
     857        clone = f" {clone}" if clone else ""
     858
     859        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
     860        return self.prepend_ctes(expression, expression_sql)
     861
     862    def clone_sql(self, expression: exp.Clone) -> str:
     863        this = self.sql(expression, "this")
     864        shallow = "SHALLOW " if expression.args.get("shallow") else ""
     865        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
     866        return f"{shallow}{keyword} {this}"
     867
     868    def describe_sql(self, expression: exp.Describe) -> str:
     869        return f"DESCRIBE {self.sql(expression, 'this')}"
     870
     871    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
     872        with_ = self.sql(expression, "with")
     873        if with_:
     874            sql = f"{with_}{self.sep()}{sql}"
     875        return sql
     876
     877    def with_sql(self, expression: exp.With) -> str:
     878        sql = self.expressions(expression, flat=True)
     879        recursive = (
     880            "RECURSIVE "
     881            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
     882            else ""
     883        )
     884
     885        return f"WITH {recursive}{sql}"
     886
     887    def cte_sql(self, expression: exp.CTE) -> str:
     888        alias = self.sql(expression, "alias")
     889        return f"{alias} AS {self.wrap(expression)}"
     890
     891    def tablealias_sql(self, expression: exp.TableAlias) -> str:
     892        alias = self.sql(expression, "this")
     893        columns = self.expressions(expression, key="columns", flat=True)
     894        columns = f"({columns})" if columns else ""
     895
     896        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
     897            alias = "_t"
     898
     899        return f"{alias}{columns}"
     900
     901    def bitstring_sql(self, expression: exp.BitString) -> str:
     902        this = self.sql(expression, "this")
     903        if self.dialect.BIT_START:
     904            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
     905        return f"{int(this, 2)}"
     906
     907    def hexstring_sql(self, expression: exp.HexString) -> str:
     908        this = self.sql(expression, "this")
     909        if self.dialect.HEX_START:
     910            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
     911        return f"{int(this, 16)}"
     912
     913    def bytestring_sql(self, expression: exp.ByteString) -> str:
     914        this = self.sql(expression, "this")
     915        if self.dialect.BYTE_START:
     916            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
     917        return this
     918
     919    def rawstring_sql(self, expression: exp.RawString) -> str:
     920        string = self.escape_str(expression.this.replace("\\", "\\\\"))
     921        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
     922
     923    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
     924        this = self.sql(expression, "this")
     925        specifier = self.sql(expression, "expression")
     926        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
     927        return f"{this}{specifier}"
     928
     929    def datatype_sql(self, expression: exp.DataType) -> str:
     930        type_value = expression.this
     931
     932        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
     933            type_sql = self.sql(expression, "kind")
     934        else:
     935            type_sql = (
     936                self.TYPE_MAPPING.get(type_value, type_value.value)
     937                if isinstance(type_value, exp.DataType.Type)
     938                else type_value
     939            )
     940
     941        nested = ""
     942        interior = self.expressions(expression, flat=True)
     943        values = ""
     944
     945        if interior:
     946            if expression.args.get("nested"):
     947                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
     948                if expression.args.get("values") is not None:
     949                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
     950                    values = self.expressions(expression, key="values", flat=True)
     951                    values = f"{delimiters[0]}{values}{delimiters[1]}"
     952            elif type_value == exp.DataType.Type.INTERVAL:
     953                nested = f" {interior}"
     954            else:
     955                nested = f"({interior})"
     956
     957        type_sql = f"{type_sql}{nested}{values}"
     958        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
     959            exp.DataType.Type.TIMETZ,
     960            exp.DataType.Type.TIMESTAMPTZ,
     961        ):
     962            type_sql = f"{type_sql} WITH TIME ZONE"
     963
     964        return type_sql
     965
     966    def directory_sql(self, expression: exp.Directory) -> str:
     967        local = "LOCAL " if expression.args.get("local") else ""
     968        row_format = self.sql(expression, "row_format")
     969        row_format = f" {row_format}" if row_format else ""
     970        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
     971
     972    def delete_sql(self, expression: exp.Delete) -> str:
     973        this = self.sql(expression, "this")
     974        this = f" FROM {this}" if this else ""
     975        using = self.sql(expression, "using")
     976        using = f" USING {using}" if using else ""
     977        where = self.sql(expression, "where")
     978        returning = self.sql(expression, "returning")
     979        limit = self.sql(expression, "limit")
     980        tables = self.expressions(expression, key="tables")
     981        tables = f" {tables}" if tables else ""
     982        if self.RETURNING_END:
     983            expression_sql = f"{this}{using}{where}{returning}{limit}"
     984        else:
     985            expression_sql = f"{returning}{this}{using}{where}{limit}"
     986        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
     987
     988    def drop_sql(self, expression: exp.Drop) -> str:
     989        this = self.sql(expression, "this")
     990        kind = expression.args["kind"]
     991        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
     992        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
     993        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
     994        cascade = " CASCADE" if expression.args.get("cascade") else ""
     995        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
     996        purge = " PURGE" if expression.args.get("purge") else ""
     997        return (
     998            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
     999        )
    1000
    1001    def except_sql(self, expression: exp.Except) -> str:
    1002        return self.prepend_ctes(
    1003            expression,
    1004            self.set_operation(expression, self.except_op(expression)),
    1005        )
    1006
    1007    def except_op(self, expression: exp.Except) -> str:
    1008        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
    1009
    1010    def fetch_sql(self, expression: exp.Fetch) -> str:
    1011        direction = expression.args.get("direction")
    1012        direction = f" {direction.upper()}" if direction else ""
    1013        count = expression.args.get("count")
    1014        count = f" {count}" if count else ""
    1015        if expression.args.get("percent"):
    1016            count = f"{count} PERCENT"
    1017        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1018        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
    1019
    1020    def filter_sql(self, expression: exp.Filter) -> str:
    1021        if self.AGGREGATE_FILTER_SUPPORTED:
    1022            this = self.sql(expression, "this")
    1023            where = self.sql(expression, "expression").strip()
    1024            return f"{this} FILTER({where})"
    1025
    1026        agg = expression.this
    1027        agg_arg = agg.this
    1028        cond = expression.expression.this
    1029        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1030        return self.sql(agg)
    1031
    1032    def hint_sql(self, expression: exp.Hint) -> str:
    1033        if not self.QUERY_HINTS:
    1034            self.unsupported("Hints are not supported")
    1035            return ""
    1036
    1037        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
    1038
    1039    def index_sql(self, expression: exp.Index) -> str:
    1040        unique = "UNIQUE " if expression.args.get("unique") else ""
    1041        primary = "PRIMARY " if expression.args.get("primary") else ""
    1042        amp = "AMP " if expression.args.get("amp") else ""
    1043        name = self.sql(expression, "this")
    1044        name = f"{name} " if name else ""
    1045        table = self.sql(expression, "table")
    1046        table = f"{self.INDEX_ON} {table}" if table else ""
    1047        using = self.sql(expression, "using")
    1048        using = f" USING {using}" if using else ""
    1049        index = "INDEX " if not table else ""
    1050        columns = self.expressions(expression, key="columns", flat=True)
    1051        columns = f"({columns})" if columns else ""
    1052        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1053        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1054        where = self.sql(expression, "where")
    1055        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"
    1056
    1057    def identifier_sql(self, expression: exp.Identifier) -> str:
    1058        text = expression.name
    1059        lower = text.lower()
    1060        text = lower if self.normalize and not expression.quoted else text
    1061        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1062        if (
    1063            expression.quoted
    1064            or self.dialect.can_identify(text, self.identify)
    1065            or lower in self.RESERVED_KEYWORDS
    1066            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1067        ):
    1068            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1069        return text
    1070
    1071    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1072        input_format = self.sql(expression, "input_format")
    1073        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1074        output_format = self.sql(expression, "output_format")
    1075        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1076        return self.sep().join((input_format, output_format))
    1077
    1078    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1079        string = self.sql(exp.Literal.string(expression.name))
    1080        return f"{prefix}{string}"
    1081
    1082    def partition_sql(self, expression: exp.Partition) -> str:
    1083        return f"PARTITION({self.expressions(expression, flat=True)})"
    1084
    1085    def properties_sql(self, expression: exp.Properties) -> str:
    1086        root_properties = []
    1087        with_properties = []
    1088
    1089        for p in expression.expressions:
    1090            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1091            if p_loc == exp.Properties.Location.POST_WITH:
    1092                with_properties.append(p)
    1093            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1094                root_properties.append(p)
    1095
    1096        return self.root_properties(
    1097            exp.Properties(expressions=root_properties)
    1098        ) + self.with_properties(exp.Properties(expressions=with_properties))
    1099
    1100    def root_properties(self, properties: exp.Properties) -> str:
    1101        if properties.expressions:
    1102            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1103        return ""
    1104
    1105    def properties(
    1106        self,
    1107        properties: exp.Properties,
    1108        prefix: str = "",
    1109        sep: str = ", ",
    1110        suffix: str = "",
    1111        wrapped: bool = True,
    1112    ) -> str:
    1113        if properties.expressions:
    1114            expressions = self.expressions(properties, sep=sep, indent=False)
    1115            if expressions:
    1116                expressions = self.wrap(expressions) if wrapped else expressions
    1117                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1118        return ""
    1119
    1120    def with_properties(self, properties: exp.Properties) -> str:
    1121        return self.properties(properties, prefix=self.seg("WITH"))
    1122
    1123    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1124        properties_locs = defaultdict(list)
    1125        for p in properties.expressions:
    1126            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1127            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1128                properties_locs[p_loc].append(p)
    1129            else:
    1130                self.unsupported(f"Unsupported property {p.key}")
    1131
    1132        return properties_locs
    1133
    1134    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1135        if isinstance(expression.this, exp.Dot):
    1136            return self.sql(expression, "this")
    1137        return f"'{expression.name}'" if string_key else expression.name
    1138
    1139    def property_sql(self, expression: exp.Property) -> str:
    1140        property_cls = expression.__class__
    1141        if property_cls == exp.Property:
    1142            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1143
    1144        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1145        if not property_name:
    1146            self.unsupported(f"Unsupported property {expression.key}")
    1147
    1148        return f"{property_name}={self.sql(expression, 'this')}"
    1149
    1150    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1151        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1152        options = f" {options}" if options else ""
    1153        return f"LIKE {self.sql(expression, 'this')}{options}"
    1154
    1155    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1156        no = "NO " if expression.args.get("no") else ""
    1157        protection = " PROTECTION" if expression.args.get("protection") else ""
    1158        return f"{no}FALLBACK{protection}"
    1159
    1160    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1161        no = "NO " if expression.args.get("no") else ""
    1162        local = expression.args.get("local")
    1163        local = f"{local} " if local else ""
    1164        dual = "DUAL " if expression.args.get("dual") else ""
    1165        before = "BEFORE " if expression.args.get("before") else ""
    1166        after = "AFTER " if expression.args.get("after") else ""
    1167        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1168
    1169    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1170        freespace = self.sql(expression, "this")
    1171        percent = " PERCENT" if expression.args.get("percent") else ""
    1172        return f"FREESPACE={freespace}{percent}"
    1173
    1174    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1175        if expression.args.get("default"):
    1176            property = "DEFAULT"
    1177        elif expression.args.get("on"):
    1178            property = "ON"
    1179        else:
    1180            property = "OFF"
    1181        return f"CHECKSUM={property}"
    1182
    1183    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1184        if expression.args.get("no"):
    1185            return "NO MERGEBLOCKRATIO"
    1186        if expression.args.get("default"):
    1187            return "DEFAULT MERGEBLOCKRATIO"
    1188
    1189        percent = " PERCENT" if expression.args.get("percent") else ""
    1190        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1191
    1192    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1193        default = expression.args.get("default")
    1194        minimum = expression.args.get("minimum")
    1195        maximum = expression.args.get("maximum")
    1196        if default or minimum or maximum:
    1197            if default:
    1198                prop = "DEFAULT"
    1199            elif minimum:
    1200                prop = "MINIMUM"
    1201            else:
    1202                prop = "MAXIMUM"
    1203            return f"{prop} DATABLOCKSIZE"
    1204        units = expression.args.get("units")
    1205        units = f" {units}" if units else ""
    1206        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1207
    1208    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1209        autotemp = expression.args.get("autotemp")
    1210        always = expression.args.get("always")
    1211        default = expression.args.get("default")
    1212        manual = expression.args.get("manual")
    1213        never = expression.args.get("never")
    1214
    1215        if autotemp is not None:
    1216            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1217        elif always:
    1218            prop = "ALWAYS"
    1219        elif default:
    1220            prop = "DEFAULT"
    1221        elif manual:
    1222            prop = "MANUAL"
    1223        elif never:
    1224            prop = "NEVER"
    1225        return f"BLOCKCOMPRESSION={prop}"
    1226
    1227    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1228        no = expression.args.get("no")
    1229        no = " NO" if no else ""
    1230        concurrent = expression.args.get("concurrent")
    1231        concurrent = " CONCURRENT" if concurrent else ""
    1232
    1233        for_ = ""
    1234        if expression.args.get("for_all"):
    1235            for_ = " FOR ALL"
    1236        elif expression.args.get("for_insert"):
    1237            for_ = " FOR INSERT"
    1238        elif expression.args.get("for_none"):
    1239            for_ = " FOR NONE"
    1240        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
    1241
    1242    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1243        if isinstance(expression.this, list):
    1244            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1245        if expression.this:
    1246            modulus = self.sql(expression, "this")
    1247            remainder = self.sql(expression, "expression")
    1248            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1249
    1250        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1251        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1252        return f"FROM ({from_expressions}) TO ({to_expressions})"
    1253
    1254    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1255        this = self.sql(expression, "this")
    1256
    1257        for_values_or_default = expression.expression
    1258        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1259            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1260        else:
    1261            for_values_or_default = " DEFAULT"
    1262
    1263        return f"PARTITION OF {this}{for_values_or_default}"
    1264
    1265    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1266        kind = expression.args.get("kind")
    1267        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1268        for_or_in = expression.args.get("for_or_in")
    1269        for_or_in = f" {for_or_in}" if for_or_in else ""
    1270        lock_type = expression.args.get("lock_type")
    1271        override = " OVERRIDE" if expression.args.get("override") else ""
    1272        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
    1273
    1274    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1275        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1276        statistics = expression.args.get("statistics")
    1277        statistics_sql = ""
    1278        if statistics is not None:
    1279            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1280        return f"{data_sql}{statistics_sql}"
    1281
    1282    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1283        sql = "WITH(SYSTEM_VERSIONING=ON"
    1284
    1285        if expression.this:
    1286            history_table = self.sql(expression, "this")
    1287            sql = f"{sql}(HISTORY_TABLE={history_table}"
    1288
    1289            if expression.expression:
    1290                data_consistency_check = self.sql(expression, "expression")
    1291                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
    1292
    1293            sql = f"{sql})"
    1294
    1295        return f"{sql})"
    1296
    1297    def insert_sql(self, expression: exp.Insert) -> str:
    1298        overwrite = expression.args.get("overwrite")
    1299
    1300        if isinstance(expression.this, exp.Directory):
    1301            this = " OVERWRITE" if overwrite else " INTO"
    1302        else:
    1303            this = " OVERWRITE TABLE" if overwrite else " INTO"
    1304
    1305        alternative = expression.args.get("alternative")
    1306        alternative = f" OR {alternative}" if alternative else ""
    1307        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1308
    1309        this = f"{this} {self.sql(expression, 'this')}"
    1310
    1311        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1312        partition_sql = (
    1313            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1314        )
    1315        where = self.sql(expression, "where")
    1316        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1317        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1318        conflict = self.sql(expression, "conflict")
    1319        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1320        returning = self.sql(expression, "returning")
    1321
    1322        if self.RETURNING_END:
    1323            expression_sql = f"{expression_sql}{conflict}{returning}"
    1324        else:
    1325            expression_sql = f"{returning}{expression_sql}{conflict}"
    1326
    1327        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1328        return self.prepend_ctes(expression, sql)
    1329
    1330    def intersect_sql(self, expression: exp.Intersect) -> str:
    1331        return self.prepend_ctes(
    1332            expression,
    1333            self.set_operation(expression, self.intersect_op(expression)),
    1334        )
    1335
    1336    def intersect_op(self, expression: exp.Intersect) -> str:
    1337        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
    1338
    1339    def introducer_sql(self, expression: exp.Introducer) -> str:
    1340        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1341
    1342    def kill_sql(self, expression: exp.Kill) -> str:
    1343        kind = self.sql(expression, "kind")
    1344        kind = f" {kind}" if kind else ""
    1345        this = self.sql(expression, "this")
    1346        this = f" {this}" if this else ""
    1347        return f"KILL{kind}{this}"
    1348
    1349    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1350        return expression.name.upper()
    1351
    1352    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1353        return expression.name.upper()
    1354
    1355    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1356        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1357        constraint = self.sql(expression, "constraint")
    1358        if constraint:
    1359            constraint = f"ON CONSTRAINT {constraint}"
    1360        key = self.expressions(expression, key="key", flat=True)
    1361        do = "" if expression.args.get("duplicate") else " DO "
    1362        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1363        expressions = self.expressions(expression, flat=True)
    1364        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1365        if expressions:
    1366            expressions = f"UPDATE {set_keyword}{expressions}"
    1367        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
    1368
    1369    def returning_sql(self, expression: exp.Returning) -> str:
    1370        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1371
    1372    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1373        fields = expression.args.get("fields")
    1374        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1375        escaped = expression.args.get("escaped")
    1376        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1377        items = expression.args.get("collection_items")
    1378        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1379        keys = expression.args.get("map_keys")
    1380        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1381        lines = expression.args.get("lines")
    1382        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1383        null = expression.args.get("null")
    1384        null = f" NULL DEFINED AS {null}" if null else ""
    1385        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1386
    1387    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1388        return f"WITH ({self.expressions(expression, flat=True)})"
    1389
    1390    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1391        this = f"{self.sql(expression, 'this')} INDEX"
    1392        target = self.sql(expression, "target")
    1393        target = f" FOR {target}" if target else ""
    1394        return f"{this}{target} ({self.expressions(expression, flat=True)})"
    1395
    1396    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1397        this = self.sql(expression, "this")
    1398        kind = self.sql(expression, "kind")
    1399        expr = self.sql(expression, "expression")
    1400        return f"{this} ({kind} => {expr})"
    1401
    1402    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1403        table = ".".join(
    1404            self.sql(part)
    1405            for part in (
    1406                expression.args.get("catalog"),
    1407                expression.args.get("db"),
    1408                expression.args.get("this"),
    1409            )
    1410            if part is not None
    1411        )
    1412
    1413        version = self.sql(expression, "version")
    1414        version = f" {version}" if version else ""
    1415        alias = self.sql(expression, "alias")
    1416        alias = f"{sep}{alias}" if alias else ""
    1417        hints = self.expressions(expression, key="hints", sep=" ")
    1418        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1419        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1420        pivots = f" {pivots}" if pivots else ""
    1421        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
    1422        laterals = self.expressions(expression, key="laterals", sep="")
    1423
    1424        file_format = self.sql(expression, "format")
    1425        if file_format:
    1426            pattern = self.sql(expression, "pattern")
    1427            pattern = f", PATTERN => {pattern}" if pattern else ""
    1428            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1429
    1430        index = self.sql(expression, "index")
    1431        index = f" AT {index}" if index else ""
    1432
    1433        ordinality = expression.args.get("ordinality") or ""
    1434        if ordinality:
    1435            ordinality = f" WITH ORDINALITY{alias}"
    1436            alias = ""
    1437
    1438        when = self.sql(expression, "when")
    1439        if when:
    1440            table = f"{table} {when}"
    1441
    1442        return f"{table}{version}{file_format}{alias}{index}{hints}{pivots}{joins}{laterals}{ordinality}"
    1443
    1444    def tablesample_sql(
    1445        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1446    ) -> str:
    1447        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1448            table = expression.this.copy()
    1449            table.set("alias", None)
    1450            this = self.sql(table)
    1451            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1452        else:
    1453            this = self.sql(expression, "this")
    1454            alias = ""
    1455
    1456        method = self.sql(expression, "method")
    1457        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1458        numerator = self.sql(expression, "bucket_numerator")
    1459        denominator = self.sql(expression, "bucket_denominator")
    1460        field = self.sql(expression, "bucket_field")
    1461        field = f" ON {field}" if field else ""
    1462        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1463        percent = self.sql(expression, "percent")
    1464        percent = f"{percent} PERCENT" if percent else ""
    1465        rows = self.sql(expression, "rows")
    1466        rows = f"{rows} ROWS" if rows else ""
    1467
    1468        size = self.sql(expression, "size")
    1469        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1470            size = f"{size} PERCENT"
    1471
    1472        seed = self.sql(expression, "seed")
    1473        seed = f" {seed_prefix} ({seed})" if seed else ""
    1474        kind = expression.args.get("kind", "TABLESAMPLE")
    1475
    1476        expr = f"{bucket}{percent}{rows}{size}"
    1477        if self.TABLESAMPLE_REQUIRES_PARENS:
    1478            expr = f"({expr})"
    1479
    1480        return f"{this} {kind} {method}{expr}{seed}{alias}"
    1481
    1482    def pivot_sql(self, expression: exp.Pivot) -> str:
    1483        expressions = self.expressions(expression, flat=True)
    1484
    1485        if expression.this:
    1486            this = self.sql(expression, "this")
    1487            if not expressions:
    1488                return f"UNPIVOT {this}"
    1489
    1490            on = f"{self.seg('ON')} {expressions}"
    1491            using = self.expressions(expression, key="using", flat=True)
    1492            using = f"{self.seg('USING')} {using}" if using else ""
    1493            group = self.sql(expression, "group")
    1494            return f"PIVOT {this}{on}{using}{group}"
    1495
    1496        alias = self.sql(expression, "alias")
    1497        alias = f" AS {alias}" if alias else ""
    1498        unpivot = expression.args.get("unpivot")
    1499        direction = "UNPIVOT" if unpivot else "PIVOT"
    1500        field = self.sql(expression, "field")
    1501        include_nulls = expression.args.get("include_nulls")
    1502        if include_nulls is not None:
    1503            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1504        else:
    1505            nulls = ""
    1506        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
    1507
    1508    def version_sql(self, expression: exp.Version) -> str:
    1509        this = f"FOR {expression.name}"
    1510        kind = expression.text("kind")
    1511        expr = self.sql(expression, "expression")
    1512        return f"{this} {kind} {expr}"
    1513
    1514    def tuple_sql(self, expression: exp.Tuple) -> str:
    1515        return f"({self.expressions(expression, flat=True)})"
    1516
    1517    def update_sql(self, expression: exp.Update) -> str:
    1518        this = self.sql(expression, "this")
    1519        set_sql = self.expressions(expression, flat=True)
    1520        from_sql = self.sql(expression, "from")
    1521        where_sql = self.sql(expression, "where")
    1522        returning = self.sql(expression, "returning")
    1523        order = self.sql(expression, "order")
    1524        limit = self.sql(expression, "limit")
    1525        if self.RETURNING_END:
    1526            expression_sql = f"{from_sql}{where_sql}{returning}"
    1527        else:
    1528            expression_sql = f"{returning}{from_sql}{where_sql}"
    1529        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1530        return self.prepend_ctes(expression, sql)
    1531
    1532    def values_sql(self, expression: exp.Values) -> str:
    1533        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1534        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
    1535            args = self.expressions(expression)
    1536            alias = self.sql(expression, "alias")
    1537            values = f"VALUES{self.seg('')}{args}"
    1538            values = (
    1539                f"({values})"
    1540                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1541                else values
    1542            )
    1543            return f"{values} AS {alias}" if alias else values
    1544
    1545        # Converts `VALUES...` expression into a series of select unions.
    1546        alias_node = expression.args.get("alias")
    1547        column_names = alias_node and alias_node.columns
    1548
    1549        selects: t.List[exp.Subqueryable] = []
    1550
    1551        for i, tup in enumerate(expression.expressions):
    1552            row = tup.expressions
    1553
    1554            if i == 0 and column_names:
    1555                row = [
    1556                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1557                ]
    1558
    1559            selects.append(exp.Select(expressions=row))
    1560
    1561        if self.pretty:
    1562            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1563            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1564            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1565            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1566            return self.subquery_sql(
    1567                subqueryable.subquery(alias_node and alias_node.this, copy=False)
    1568            )
    1569
    1570        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1571        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1572        return f"({unions}){alias}"
    1573
    1574    def var_sql(self, expression: exp.Var) -> str:
    1575        return self.sql(expression, "this")
    1576
    1577    def into_sql(self, expression: exp.Into) -> str:
    1578        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1579        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    1580        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    1581
    1582    def from_sql(self, expression: exp.From) -> str:
    1583        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    1584
    1585    def group_sql(self, expression: exp.Group) -> str:
    1586        group_by = self.op_expressions("GROUP BY", expression)
    1587
    1588        if expression.args.get("all"):
    1589            return f"{group_by} ALL"
    1590
    1591        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1592        grouping_sets = (
    1593            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1594        )
    1595
    1596        cube = expression.args.get("cube", [])
    1597        if seq_get(cube, 0) is True:
    1598            return f"{group_by}{self.seg('WITH CUBE')}"
    1599        else:
    1600            cube_sql = self.expressions(expression, key="cube", indent=False)
    1601            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1602
    1603        rollup = expression.args.get("rollup", [])
    1604        if seq_get(rollup, 0) is True:
    1605            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1606        else:
    1607            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1608            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1609
    1610        groupings = csv(
    1611            grouping_sets,
    1612            cube_sql,
    1613            rollup_sql,
    1614            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1615            sep=self.GROUPINGS_SEP,
    1616        )
    1617
    1618        if expression.args.get("expressions") and groupings:
    1619            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1620
    1621        return f"{group_by}{groupings}"
    1622
    1623    def having_sql(self, expression: exp.Having) -> str:
    1624        this = self.indent(self.sql(expression, "this"))
    1625        return f"{self.seg('HAVING')}{self.sep()}{this}"
    1626
    1627    def connect_sql(self, expression: exp.Connect) -> str:
    1628        start = self.sql(expression, "start")
    1629        start = self.seg(f"START WITH {start}") if start else ""
    1630        connect = self.sql(expression, "connect")
    1631        connect = self.seg(f"CONNECT BY {connect}")
    1632        return start + connect
    1633
    1634    def prior_sql(self, expression: exp.Prior) -> str:
    1635        return f"PRIOR {self.sql(expression, 'this')}"
    1636
    1637    def join_sql(self, expression: exp.Join) -> str:
    1638        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1639            side = None
    1640        else:
    1641            side = expression.side
    1642
    1643        op_sql = " ".join(
    1644            op
    1645            for op in (
    1646                expression.method,
    1647                "GLOBAL" if expression.args.get("global") else None,
    1648                side,
    1649                expression.kind,
    1650                expression.hint if self.JOIN_HINTS else None,
    1651            )
    1652            if op
    1653        )
    1654        on_sql = self.sql(expression, "on")
    1655        using = expression.args.get("using")
    1656
    1657        if not on_sql and using:
    1658            on_sql = csv(*(self.sql(column) for column in using))
    1659
    1660        this_sql = self.sql(expression, "this")
    1661
    1662        if on_sql:
    1663            on_sql = self.indent(on_sql, skip_first=True)
    1664            space = self.seg(" " * self.pad) if self.pretty else " "
    1665            if using:
    1666                on_sql = f"{space}USING ({on_sql})"
    1667            else:
    1668                on_sql = f"{space}ON {on_sql}"
    1669        elif not op_sql:
    1670            return f", {this_sql}"
    1671
    1672        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1673        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
    1674
    1675    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    1676        args = self.expressions(expression, flat=True)
    1677        args = f"({args})" if len(args.split(",")) > 1 else args
    1678        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    1679
    1680    def lateral_sql(self, expression: exp.Lateral) -> str:
    1681        this = self.sql(expression, "this")
    1682
    1683        if expression.args.get("view"):
    1684            alias = expression.args["alias"]
    1685            columns = self.expressions(alias, key="columns", flat=True)
    1686            table = f" {alias.name}" if alias.name else ""
    1687            columns = f" AS {columns}" if columns else ""
    1688            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1689            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1690
    1691        alias = self.sql(expression, "alias")
    1692        alias = f" AS {alias}" if alias else ""
    1693        return f"LATERAL {this}{alias}"
    1694
    1695    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1696        this = self.sql(expression, "this")
    1697
    1698        args = [
    1699            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    1700            for e in (expression.args.get(k) for k in ("offset", "expression"))
    1701            if e
    1702        ]
    1703
    1704        args_sql = ", ".join(self.sql(e) for e in args)
    1705        args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql
    1706        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}"
    1707
    1708    def offset_sql(self, expression: exp.Offset) -> str:
    1709        this = self.sql(expression, "this")
    1710        expression = expression.expression
    1711        expression = (
    1712            self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression
    1713        )
    1714        return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"
    1715
    1716    def setitem_sql(self, expression: exp.SetItem) -> str:
    1717        kind = self.sql(expression, "kind")
    1718        kind = f"{kind} " if kind else ""
    1719        this = self.sql(expression, "this")
    1720        expressions = self.expressions(expression)
    1721        collate = self.sql(expression, "collate")
    1722        collate = f" COLLATE {collate}" if collate else ""
    1723        global_ = "GLOBAL " if expression.args.get("global") else ""
    1724        return f"{global_}{kind}{this}{expressions}{collate}"
    1725
    1726    def set_sql(self, expression: exp.Set) -> str:
    1727        expressions = (
    1728            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1729        )
    1730        tag = " TAG" if expression.args.get("tag") else ""
    1731        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
    1732
    1733    def pragma_sql(self, expression: exp.Pragma) -> str:
    1734        return f"PRAGMA {self.sql(expression, 'this')}"
    1735
    1736    def lock_sql(self, expression: exp.Lock) -> str:
    1737        if not self.LOCKING_READS_SUPPORTED:
    1738            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1739            return ""
    1740
    1741        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1742        expressions = self.expressions(expression, flat=True)
    1743        expressions = f" OF {expressions}" if expressions else ""
    1744        wait = expression.args.get("wait")
    1745
    1746        if wait is not None:
    1747            if isinstance(wait, exp.Literal):
    1748                wait = f" WAIT {self.sql(wait)}"
    1749            else:
    1750                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1751
    1752        return f"{lock_type}{expressions}{wait or ''}"
    1753
    1754    def literal_sql(self, expression: exp.Literal) -> str:
    1755        text = expression.this or ""
    1756        if expression.is_string:
    1757            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    1758        return text
    1759
    1760    def escape_str(self, text: str) -> str:
    1761        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
    1762        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
    1763            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1764        elif self.pretty:
    1765            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1766        return text
    1767
    1768    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1769        local = " LOCAL" if expression.args.get("local") else ""
    1770        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1771        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1772        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1773        partition = self.sql(expression, "partition")
    1774        partition = f" {partition}" if partition else ""
    1775        input_format = self.sql(expression, "input_format")
    1776        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1777        serde = self.sql(expression, "serde")
    1778        serde = f" SERDE {serde}" if serde else ""
    1779        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    1780
    1781    def null_sql(self, *_) -> str:
    1782        return "NULL"
    1783
    1784    def boolean_sql(self, expression: exp.Boolean) -> str:
    1785        return "TRUE" if expression.this else "FALSE"
    1786
    1787    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1788        this = self.sql(expression, "this")
    1789        this = f"{this} " if this else this
    1790        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
    1791
    1792    def cluster_sql(self, expression: exp.Cluster) -> str:
    1793        return self.op_expressions("CLUSTER BY", expression)
    1794
    1795    def distribute_sql(self, expression: exp.Distribute) -> str:
    1796        return self.op_expressions("DISTRIBUTE BY", expression)
    1797
    1798    def sort_sql(self, expression: exp.Sort) -> str:
    1799        return self.op_expressions("SORT BY", expression)
    1800
    1801    def ordered_sql(self, expression: exp.Ordered) -> str:
    1802        desc = expression.args.get("desc")
    1803        asc = not desc
    1804
    1805        nulls_first = expression.args.get("nulls_first")
    1806        nulls_last = not nulls_first
    1807        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    1808        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    1809        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    1810
    1811        this = self.sql(expression, "this")
    1812
    1813        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    1814        nulls_sort_change = ""
    1815        if nulls_first and (
    1816            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1817        ):
    1818            nulls_sort_change = " NULLS FIRST"
    1819        elif (
    1820            nulls_last
    1821            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1822            and not nulls_are_last
    1823        ):
    1824            nulls_sort_change = " NULLS LAST"
    1825
    1826        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    1827        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1828            null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    1829            this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    1830            nulls_sort_change = ""
    1831
    1832        return f"{this}{sort_order}{nulls_sort_change}"
    1833
    1834    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1835        partition = self.partition_by_sql(expression)
    1836        order = self.sql(expression, "order")
    1837        measures = self.expressions(expression, key="measures")
    1838        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1839        rows = self.sql(expression, "rows")
    1840        rows = self.seg(rows) if rows else ""
    1841        after = self.sql(expression, "after")
    1842        after = self.seg(after) if after else ""
    1843        pattern = self.sql(expression, "pattern")
    1844        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1845        definition_sqls = [
    1846            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1847            for definition in expression.args.get("define", [])
    1848        ]
    1849        definitions = self.expressions(sqls=definition_sqls)
    1850        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1851        body = "".join(
    1852            (
    1853                partition,
    1854                order,
    1855                measures,
    1856                rows,
    1857                after,
    1858                pattern,
    1859                define,
    1860            )
    1861        )
    1862        alias = self.sql(expression, "alias")
    1863        alias = f" {alias}" if alias else ""
    1864        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    1865
    1866    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1867        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
    1868
    1869        # If the limit is generated as TOP, we need to ensure it's not generated twice
    1870        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
    1871
    1872        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1873            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    1874        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1875            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    1876
    1877        fetch = isinstance(limit, exp.Fetch)
    1878
    1879        offset_limit_modifiers = (
    1880            self.offset_limit_modifiers(expression, fetch, limit)
    1881            if with_offset_limit_modifiers
    1882            else []
    1883        )
    1884
    1885        return csv(
    1886            *sqls,
    1887            *[self.sql(join) for join in expression.args.get("joins") or []],
    1888            self.sql(expression, "connect"),
    1889            self.sql(expression, "match"),
    1890            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1891            self.sql(expression, "where"),
    1892            self.sql(expression, "group"),
    1893            self.sql(expression, "having"),
    1894            *self.after_having_modifiers(expression),
    1895            self.sql(expression, "order"),
    1896            *offset_limit_modifiers,
    1897            *self.after_limit_modifiers(expression),
    1898            sep="",
    1899        )
    1900
    1901    def offset_limit_modifiers(
    1902        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    1903    ) -> t.List[str]:
    1904        return [
    1905            self.sql(expression, "offset") if fetch else self.sql(limit),
    1906            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1907        ]
    1908
    1909    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1910        return [
    1911            self.sql(expression, "qualify"),
    1912            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1913            if expression.args.get("windows")
    1914            else "",
    1915            self.sql(expression, "distribute"),
    1916            self.sql(expression, "sort"),
    1917            self.sql(expression, "cluster"),
    1918        ]
    1919
    1920    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1921        locks = self.expressions(expression, key="locks", sep=" ")
    1922        locks = f" {locks}" if locks else ""
    1923        return [locks, self.sql(expression, "sample")]
    1924
    1925    def select_sql(self, expression: exp.Select) -> str:
    1926        hint = self.sql(expression, "hint")
    1927        distinct = self.sql(expression, "distinct")
    1928        distinct = f" {distinct}" if distinct else ""
    1929        kind = self.sql(expression, "kind").upper()
    1930        limit = expression.args.get("limit")
    1931        top = (
    1932            self.limit_sql(limit, top=True)
    1933            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
    1934            else ""
    1935        )
    1936
    1937        expressions = self.expressions(expression)
    1938
    1939        if kind:
    1940            if kind in self.SELECT_KINDS:
    1941                kind = f" AS {kind}"
    1942            else:
    1943                if kind == "STRUCT":
    1944                    expressions = self.expressions(
    1945                        sqls=[
    1946                            self.sql(
    1947                                exp.Struct(
    1948                                    expressions=[
    1949                                        exp.column(e.output_name).eq(
    1950                                            e.this if isinstance(e, exp.Alias) else e
    1951                                        )
    1952                                        for e in expression.expressions
    1953                                    ]
    1954                                )
    1955                            )
    1956                        ]
    1957                    )
    1958                kind = ""
    1959
    1960        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    1961        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    1962        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    1963        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1964        sql = self.query_modifiers(
    1965            expression,
    1966            f"SELECT{top_distinct}{kind}{expressions}",
    1967            self.sql(expression, "into", comment=False),
    1968            self.sql(expression, "from", comment=False),
    1969        )
    1970        return self.prepend_ctes(expression, sql)
    1971
    1972    def schema_sql(self, expression: exp.Schema) -> str:
    1973        this = self.sql(expression, "this")
    1974        sql = self.schema_columns_sql(expression)
    1975        return f"{this} {sql}" if this and sql else this or sql
    1976
    1977    def schema_columns_sql(self, expression: exp.Schema) -> str:
    1978        if expression.expressions:
    1979            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1980        return ""
    1981
    1982    def star_sql(self, expression: exp.Star) -> str:
    1983        except_ = self.expressions(expression, key="except", flat=True)
    1984        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1985        replace = self.expressions(expression, key="replace", flat=True)
    1986        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1987        return f"*{except_}{replace}"
    1988
    1989    def parameter_sql(self, expression: exp.Parameter) -> str:
    1990        this = self.sql(expression, "this")
    1991        return f"{self.PARAMETER_TOKEN}{this}"
    1992
    1993    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1994        this = self.sql(expression, "this")
    1995        kind = expression.text("kind")
    1996        if kind:
    1997            kind = f"{kind}."
    1998        return f"@@{kind}{this}"
    1999
    2000    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2001        return f":{expression.name}" if expression.name else "?"
    2002
    2003    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2004        alias = self.sql(expression, "alias")
    2005        alias = f"{sep}{alias}" if alias else ""
    2006
    2007        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2008        pivots = f" {pivots}" if pivots else ""
    2009
    2010        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2011        return self.prepend_ctes(expression, sql)
    2012
    2013    def qualify_sql(self, expression: exp.Qualify) -> str:
    2014        this = self.indent(self.sql(expression, "this"))
    2015        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    2016
    2017    def union_sql(self, expression: exp.Union) -> str:
    2018        return self.prepend_ctes(
    2019            expression,
    2020            self.set_operation(expression, self.union_op(expression)),
    2021        )
    2022
    2023    def union_op(self, expression: exp.Union) -> str:
    2024        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2025        kind = kind if expression.args.get("distinct") else " ALL"
    2026        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2027        return f"UNION{kind}{by_name}"
    2028
    2029    def unnest_sql(self, expression: exp.Unnest) -> str:
    2030        args = self.expressions(expression, flat=True)
    2031
    2032        alias = expression.args.get("alias")
    2033        offset = expression.args.get("offset")
    2034
    2035        if self.UNNEST_WITH_ORDINALITY:
    2036            if alias and isinstance(offset, exp.Expression):
    2037                alias.append("columns", offset)
    2038
    2039        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2040            columns = alias.columns
    2041            alias = self.sql(columns[0]) if columns else ""
    2042        else:
    2043            alias = self.sql(alias)
    2044
    2045        alias = f" AS {alias}" if alias else alias
    2046        if self.UNNEST_WITH_ORDINALITY:
    2047            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2048        else:
    2049            if isinstance(offset, exp.Expression):
    2050                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2051            elif offset:
    2052                suffix = f"{alias} WITH OFFSET"
    2053            else:
    2054                suffix = alias
    2055
    2056        return f"UNNEST({args}){suffix}"
    2057
    2058    def where_sql(self, expression: exp.Where) -> str:
    2059        this = self.indent(self.sql(expression, "this"))
    2060        return f"{self.seg('WHERE')}{self.sep()}{this}"
    2061
    2062    def window_sql(self, expression: exp.Window) -> str:
    2063        this = self.sql(expression, "this")
    2064        partition = self.partition_by_sql(expression)
    2065        order = expression.args.get("order")
    2066        order = self.order_sql(order, flat=True) if order else ""
    2067        spec = self.sql(expression, "spec")
    2068        alias = self.sql(expression, "alias")
    2069        over = self.sql(expression, "over") or "OVER"
    2070
    2071        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2072
    2073        first = expression.args.get("first")
    2074        if first is None:
    2075            first = ""
    2076        else:
    2077            first = "FIRST" if first else "LAST"
    2078
    2079        if not partition and not order and not spec and alias:
    2080            return f"{this} {alias}"
    2081
    2082        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2083        return f"{this} ({args})"
    2084
    2085    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2086        partition = self.expressions(expression, key="partition_by", flat=True)
    2087        return f"PARTITION BY {partition}" if partition else ""
    2088
    2089    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2090        kind = self.sql(expression, "kind")
    2091        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2092        end = (
    2093            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2094            or "CURRENT ROW"
    2095        )
    2096        return f"{kind} BETWEEN {start} AND {end}"
    2097
    2098    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2099        this = self.sql(expression, "this")
    2100        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2101        return f"{this} WITHIN GROUP ({expression_sql})"
    2102
    2103    def between_sql(self, expression: exp.Between) -> str:
    2104        this = self.sql(expression, "this")
    2105        low = self.sql(expression, "low")
    2106        high = self.sql(expression, "high")
    2107        return f"{this} BETWEEN {low} AND {high}"
    2108
    2109    def bracket_sql(self, expression: exp.Bracket) -> str:
    2110        expressions = apply_index_offset(
    2111            expression.this,
    2112            expression.expressions,
    2113            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2114        )
    2115        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2116        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    2117
    2118    def all_sql(self, expression: exp.All) -> str:
    2119        return f"ALL {self.wrap(expression)}"
    2120
    2121    def any_sql(self, expression: exp.Any) -> str:
    2122        this = self.sql(expression, "this")
    2123        if isinstance(expression.this, exp.Subqueryable):
    2124            this = self.wrap(this)
    2125        return f"ANY {this}"
    2126
    2127    def exists_sql(self, expression: exp.Exists) -> str:
    2128        return f"EXISTS{self.wrap(expression)}"
    2129
    2130    def case_sql(self, expression: exp.Case) -> str:
    2131        this = self.sql(expression, "this")
    2132        statements = [f"CASE {this}" if this else "CASE"]
    2133
    2134        for e in expression.args["ifs"]:
    2135            statements.append(f"WHEN {self.sql(e, 'this')}")
    2136            statements.append(f"THEN {self.sql(e, 'true')}")
    2137
    2138        default = self.sql(expression, "default")
    2139
    2140        if default:
    2141            statements.append(f"ELSE {default}")
    2142
    2143        statements.append("END")
    2144
    2145        if self.pretty and self.text_width(statements) > self.max_text_width:
    2146            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2147
    2148        return " ".join(statements)
    2149
    2150    def constraint_sql(self, expression: exp.Constraint) -> str:
    2151        this = self.sql(expression, "this")
    2152        expressions = self.expressions(expression, flat=True)
    2153        return f"CONSTRAINT {this} {expressions}"
    2154
    2155    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2156        order = expression.args.get("order")
    2157        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2158        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    2159
    2160    def extract_sql(self, expression: exp.Extract) -> str:
    2161        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2162        expression_sql = self.sql(expression, "expression")
    2163        return f"EXTRACT({this} FROM {expression_sql})"
    2164
    2165    def trim_sql(self, expression: exp.Trim) -> str:
    2166        trim_type = self.sql(expression, "position")
    2167
    2168        if trim_type == "LEADING":
    2169            return self.func("LTRIM", expression.this)
    2170        elif trim_type == "TRAILING":
    2171            return self.func("RTRIM", expression.this)
    2172        else:
    2173            return self.func("TRIM", expression.this, expression.expression)
    2174
    2175    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2176        args = expression.expressions
    2177        if isinstance(expression, exp.ConcatWs):
    2178            args = args[1:]  # Skip the delimiter
    2179
    2180        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2181            args = [exp.cast(e, "text") for e in args]
    2182
    2183        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2184            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2185
    2186        return args
    2187
    2188    def concat_sql(self, expression: exp.Concat) -> str:
    2189        expressions = self.convert_concat_args(expression)
    2190
    2191        # Some dialects don't allow a single-argument CONCAT call
    2192        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2193            return self.sql(expressions[0])
    2194
    2195        return self.func("CONCAT", *expressions)
    2196
    2197    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2198        return self.func(
    2199            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2200        )
    2201
    2202    def check_sql(self, expression: exp.Check) -> str:
    2203        this = self.sql(expression, key="this")
    2204        return f"CHECK ({this})"
    2205
    2206    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2207        expressions = self.expressions(expression, flat=True)
    2208        reference = self.sql(expression, "reference")
    2209        reference = f" {reference}" if reference else ""
    2210        delete = self.sql(expression, "delete")
    2211        delete = f" ON DELETE {delete}" if delete else ""
    2212        update = self.sql(expression, "update")
    2213        update = f" ON UPDATE {update}" if update else ""
    2214        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
    2215
    2216    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2217        expressions = self.expressions(expression, flat=True)
    2218        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2219        options = f" {options}" if options else ""
    2220        return f"PRIMARY KEY ({expressions}){options}"
    2221
    2222    def if_sql(self, expression: exp.If) -> str:
    2223        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
    2224
    2225    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2226        modifier = expression.args.get("modifier")
    2227        modifier = f" {modifier}" if modifier else ""
    2228        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    2229
    2230    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2231        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
    2232
    2233    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2234        return f"{self.sql(expression, 'this')} FORMAT JSON"
    2235
    2236    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    2237        null_handling = expression.args.get("null_handling")
    2238        null_handling = f" {null_handling}" if null_handling else ""
    2239        unique_keys = expression.args.get("unique_keys")
    2240        if unique_keys is not None:
    2241            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2242        else:
    2243            unique_keys = ""
    2244        return_type = self.sql(expression, "return_type")
    2245        return_type = f" RETURNING {return_type}" if return_type else ""
    2246        encoding = self.sql(expression, "encoding")
    2247        encoding = f" ENCODING {encoding}" if encoding else ""
    2248        return self.func(
    2249            "JSON_OBJECT",
    2250            *expression.expressions,
    2251            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2252        )
    2253
    2254    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2255        null_handling = expression.args.get("null_handling")
    2256        null_handling = f" {null_handling}" if null_handling else ""
    2257        return_type = self.sql(expression, "return_type")
    2258        return_type = f" RETURNING {return_type}" if return_type else ""
    2259        strict = " STRICT" if expression.args.get("strict") else ""
    2260        return self.func(
    2261            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2262        )
    2263
    2264    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2265        this = self.sql(expression, "this")
    2266        order = self.sql(expression, "order")
    2267        null_handling = expression.args.get("null_handling")
    2268        null_handling = f" {null_handling}" if null_handling else ""
    2269        return_type = self.sql(expression, "return_type")
    2270        return_type = f" RETURNING {return_type}" if return_type else ""
    2271        strict = " STRICT" if expression.args.get("strict") else ""
    2272        return self.func(
    2273            "JSON_ARRAYAGG",
    2274            this,
    2275            suffix=f"{order}{null_handling}{return_type}{strict})",
    2276        )
    2277
    2278    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2279        path = self.sql(expression, "path")
    2280        path = f" PATH {path}" if path else ""
    2281        nested_schema = self.sql(expression, "nested_schema")
    2282
    2283        if nested_schema:
    2284            return f"NESTED{path} {nested_schema}"
    2285
    2286        this = self.sql(expression, "this")
    2287        kind = self.sql(expression, "kind")
    2288        kind = f" {kind}" if kind else ""
    2289        return f"{this}{kind}{path}"
    2290
    2291    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    2292        return self.func("COLUMNS", *expression.expressions)
    2293
    2294    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2295        this = self.sql(expression, "this")
    2296        path = self.sql(expression, "path")
    2297        path = f", {path}" if path else ""
    2298        error_handling = expression.args.get("error_handling")
    2299        error_handling = f" {error_handling}" if error_handling else ""
    2300        empty_handling = expression.args.get("empty_handling")
    2301        empty_handling = f" {empty_handling}" if empty_handling else ""
    2302        schema = self.sql(expression, "schema")
    2303        return self.func(
    2304            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2305        )
    2306
    2307    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2308        this = self.sql(expression, "this")
    2309        kind = self.sql(expression, "kind")
    2310        path = self.sql(expression, "path")
    2311        path = f" {path}" if path else ""
    2312        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2313        return f"{this} {kind}{path}{as_json}"
    2314
    2315    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2316        this = self.sql(expression, "this")
    2317        path = self.sql(expression, "path")
    2318        path = f", {path}" if path else ""
    2319        expressions = self.expressions(expression)
    2320        with_ = (
    2321            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2322            if expressions
    2323            else ""
    2324        )
    2325        return f"OPENJSON({this}{path}){with_}"
    2326
    2327    def in_sql(self, expression: exp.In) -> str:
    2328        query = expression.args.get("query")
    2329        unnest = expression.args.get("unnest")
    2330        field = expression.args.get("field")
    2331        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2332
    2333        if query:
    2334            in_sql = self.wrap(query)
    2335        elif unnest:
    2336            in_sql = self.in_unnest_op(unnest)
    2337        elif field:
    2338            in_sql = self.sql(field)
    2339        else:
    2340            in_sql = f"({self.expressions(expression, flat=True)})"
    2341
    2342        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    2343
    2344    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    2345        return f"(SELECT {self.sql(unnest)})"
    2346
    2347    def interval_sql(self, expression: exp.Interval) -> str:
    2348        unit = self.sql(expression, "unit")
    2349        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2350            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    2351        unit = f" {unit}" if unit else ""
    2352
    2353        if self.SINGLE_STRING_INTERVAL:
    2354            this = expression.this.name if expression.this else ""
    2355            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2356
    2357        this = self.sql(expression, "this")
    2358        if this:
    2359            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2360            this = f" {this}" if unwrapped else f" ({this})"
    2361
    2362        return f"INTERVAL{this}{unit}"
    2363
    2364    def return_sql(self, expression: exp.Return) -> str:
    2365        return f"RETURN {self.sql(expression, 'this')}"
    2366
    2367    def reference_sql(self, expression: exp.Reference) -> str:
    2368        this = self.sql(expression, "this")
    2369        expressions = self.expressions(expression, flat=True)
    2370        expressions = f"({expressions})" if expressions else ""
    2371        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2372        options = f" {options}" if options else ""
    2373        return f"REFERENCES {this}{expressions}{options}"
    2374
    2375    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    2376        return self.func(expression.name, *expression.expressions)
    2377
    2378    def paren_sql(self, expression: exp.Paren) -> str:
    2379        if isinstance(expression.unnest(), exp.Select):
    2380            sql = self.wrap(expression)
    2381        else:
    2382            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2383            sql = f"({sql}{self.seg(')', sep='')}"
    2384
    2385        return self.prepend_ctes(expression, sql)
    2386
    2387    def neg_sql(self, expression: exp.Neg) -> str:
    2388        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    2389        this_sql = self.sql(expression, "this")
    2390        sep = " " if this_sql[0] == "-" else ""
    2391        return f"-{sep}{this_sql}"
    2392
    2393    def not_sql(self, expression: exp.Not) -> str:
    2394        return f"NOT {self.sql(expression, 'this')}"
    2395
    2396    def alias_sql(self, expression: exp.Alias) -> str:
    2397        alias = self.sql(expression, "alias")
    2398        alias = f" AS {alias}" if alias else ""
    2399        return f"{self.sql(expression, 'this')}{alias}"
    2400
    2401    def aliases_sql(self, expression: exp.Aliases) -> str:
    2402        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    2403
    2404    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2405        this = self.sql(expression, "this")
    2406        zone = self.sql(expression, "zone")
    2407        return f"{this} AT TIME ZONE {zone}"
    2408
    2409    def add_sql(self, expression: exp.Add) -> str:
    2410        return self.binary(expression, "+")
    2411
    2412    def and_sql(self, expression: exp.And) -> str:
    2413        return self.connector_sql(expression, "AND")
    2414
    2415    def xor_sql(self, expression: exp.Xor) -> str:
    2416        return self.connector_sql(expression, "XOR")
    2417
    2418    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2419        if not self.pretty:
    2420            return self.binary(expression, op)
    2421
    2422        sqls = tuple(
    2423            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2424            for i, e in enumerate(expression.flatten(unnest=False))
    2425        )
    2426
    2427        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2428        return f"{sep}{op} ".join(sqls)
    2429
    2430    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    2431        return self.binary(expression, "&")
    2432
    2433    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    2434        return self.binary(expression, "<<")
    2435
    2436    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    2437        return f"~{self.sql(expression, 'this')}"
    2438
    2439    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    2440        return self.binary(expression, "|")
    2441
    2442    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    2443        return self.binary(expression, ">>")
    2444
    2445    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    2446        return self.binary(expression, "^")
    2447
    2448    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2449        format_sql = self.sql(expression, "format")
    2450        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2451        to_sql = self.sql(expression, "to")
    2452        to_sql = f" {to_sql}" if to_sql else ""
    2453        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
    2454
    2455    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    2456        zone = self.sql(expression, "this")
    2457        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    2458
    2459    def collate_sql(self, expression: exp.Collate) -> str:
    2460        if self.COLLATE_IS_FUNC:
    2461            return self.function_fallback_sql(expression)
    2462        return self.binary(expression, "COLLATE")
    2463
    2464    def command_sql(self, expression: exp.Command) -> str:
    2465        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
    2466
    2467    def comment_sql(self, expression: exp.Comment) -> str:
    2468        this = self.sql(expression, "this")
    2469        kind = expression.args["kind"]
    2470        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2471        expression_sql = self.sql(expression, "expression")
    2472        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
    2473
    2474    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2475        this = self.sql(expression, "this")
    2476        delete = " DELETE" if expression.args.get("delete") else ""
    2477        recompress = self.sql(expression, "recompress")
    2478        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2479        to_disk = self.sql(expression, "to_disk")
    2480        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2481        to_volume = self.sql(expression, "to_volume")
    2482        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2483        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    2484
    2485    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2486        where = self.sql(expression, "where")
    2487        group = self.sql(expression, "group")
    2488        aggregates = self.expressions(expression, key="aggregates")
    2489        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2490
    2491        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2492            return f"TTL {self.expressions(expression, flat=True)}"
    2493
    2494        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    2495
    2496    def transaction_sql(self, expression: exp.Transaction) -> str:
    2497        return "BEGIN"
    2498
    2499    def commit_sql(self, expression: exp.Commit) -> str:
    2500        chain = expression.args.get("chain")
    2501        if chain is not None:
    2502            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2503
    2504        return f"COMMIT{chain or ''}"
    2505
    2506    def rollback_sql(self, expression: exp.Rollback) -> str:
    2507        savepoint = expression.args.get("savepoint")
    2508        savepoint = f" TO {savepoint}" if savepoint else ""
    2509        return f"ROLLBACK{savepoint}"
    2510
    2511    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2512        this = self.sql(expression, "this")
    2513
    2514        dtype = self.sql(expression, "dtype")
    2515        if dtype:
    2516            collate = self.sql(expression, "collate")
    2517            collate = f" COLLATE {collate}" if collate else ""
    2518            using = self.sql(expression, "using")
    2519            using = f" USING {using}" if using else ""
    2520            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2521
    2522        default = self.sql(expression, "default")
    2523        if default:
    2524            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2525
    2526        if not expression.args.get("drop"):
    2527            self.unsupported("Unsupported ALTER COLUMN syntax")
    2528
    2529        return f"ALTER COLUMN {this} DROP DEFAULT"
    2530
    2531    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2532        if not self.RENAME_TABLE_WITH_DB:
    2533            # Remove db from tables
    2534            expression = expression.transform(
    2535                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2536            )
    2537        this = self.sql(expression, "this")
    2538        return f"RENAME TO {this}"
    2539
    2540    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2541        actions = expression.args["actions"]
    2542
    2543        if isinstance(actions[0], exp.ColumnDef):
    2544            actions = self.add_column_sql(expression)
    2545        elif isinstance(actions[0], exp.Schema):
    2546            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2547        elif isinstance(actions[0], exp.Delete):
    2548            actions = self.expressions(expression, key="actions", flat=True)
    2549        else:
    2550            actions = self.expressions(expression, key="actions", flat=True)
    2551
    2552        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2553        only = " ONLY" if expression.args.get("only") else ""
    2554        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
    2555
    2556    def add_column_sql(self, expression: exp.AlterTable) -> str:
    2557        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    2558            return self.expressions(
    2559                expression,
    2560                key="actions",
    2561                prefix="ADD COLUMN ",
    2562            )
    2563        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
    2564
    2565    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2566        expressions = self.expressions(expression)
    2567        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2568        return f"DROP{exists}{expressions}"
    2569
    2570    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2571        this = self.sql(expression, "this")
    2572        expression_ = self.sql(expression, "expression")
    2573        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2574
    2575        enforced = expression.args.get("enforced")
    2576        if enforced is not None:
    2577            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2578
    2579        return f"{add_constraint} {expression_}"
    2580
    2581    def distinct_sql(self, expression: exp.Distinct) -> str:
    2582        this = self.expressions(expression, flat=True)
    2583        this = f" {this}" if this else ""
    2584
    2585        on = self.sql(expression, "on")
    2586        on = f" ON {on}" if on else ""
    2587        return f"DISTINCT{this}{on}"
    2588
    2589    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    2590        return f"{self.sql(expression, 'this')} IGNORE NULLS"
    2591
    2592    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    2593        return f"{self.sql(expression, 'this')} RESPECT NULLS"
    2594
    2595    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2596        return self.sql(
    2597            exp.Cast(
    2598                this=exp.Div(this=expression.this, expression=expression.expression),
    2599                to=exp.DataType(this=exp.DataType.Type.INT),
    2600            )
    2601        )
    2602
    2603    def dpipe_sql(self, expression: exp.DPipe) -> str:
    2604        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2605            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
    2606        return self.binary(expression, "||")
    2607
    2608    def div_sql(self, expression: exp.Div) -> str:
    2609        l, r = expression.left, expression.right
    2610
    2611        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    2612            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    2613
    2614        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    2615            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
    2616                *exp.DataType.FLOAT_TYPES
    2617            ):
    2618                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    2619
    2620        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    2621            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    2622                return self.sql(
    2623                    exp.cast(
    2624                        l / r,
    2625                        to=exp.DataType.Type.BIGINT,
    2626                    )
    2627                )
    2628
    2629        return self.binary(expression, "/")
    2630
    2631    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    2632        return self.binary(expression, "OVERLAPS")
    2633
    2634    def distance_sql(self, expression: exp.Distance) -> str:
    2635        return self.binary(expression, "<->")
    2636
    2637    def dot_sql(self, expression: exp.Dot) -> str:
    2638        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    2639
    2640    def eq_sql(self, expression: exp.EQ) -> str:
    2641        return self.binary(expression, "=")
    2642
    2643    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    2644        return self.binary(expression, ":=")
    2645
    2646    def escape_sql(self, expression: exp.Escape) -> str:
    2647        return self.binary(expression, "ESCAPE")
    2648
    2649    def glob_sql(self, expression: exp.Glob) -> str:
    2650        return self.binary(expression, "GLOB")
    2651
    2652    def gt_sql(self, expression: exp.GT) -> str:
    2653        return self.binary(expression, ">")
    2654
    2655    def gte_sql(self, expression: exp.GTE) -> str:
    2656        return self.binary(expression, ">=")
    2657
    2658    def ilike_sql(self, expression: exp.ILike) -> str:
    2659        return self.binary(expression, "ILIKE")
    2660
    2661    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    2662        return self.binary(expression, "ILIKE ANY")
    2663
    2664    def is_sql(self, expression: exp.Is) -> str:
    2665        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    2666            return self.sql(
    2667                expression.this if expression.expression.this else exp.not_(expression.this)
    2668            )
    2669        return self.binary(expression, "IS")
    2670
    2671    def like_sql(self, expression: exp.Like) -> str:
    2672        return self.binary(expression, "LIKE")
    2673
    2674    def likeany_sql(self, expression: exp.LikeAny) -> str:
    2675        return self.binary(expression, "LIKE ANY")
    2676
    2677    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    2678        return self.binary(expression, "SIMILAR TO")
    2679
    2680    def lt_sql(self, expression: exp.LT) -> str:
    2681        return self.binary(expression, "<")
    2682
    2683    def lte_sql(self, expression: exp.LTE) -> str:
    2684        return self.binary(expression, "<=")
    2685
    2686    def mod_sql(self, expression: exp.Mod) -> str:
    2687        return self.binary(expression, "%")
    2688
    2689    def mul_sql(self, expression: exp.Mul) -> str:
    2690        return self.binary(expression, "*")
    2691
    2692    def neq_sql(self, expression: exp.NEQ) -> str:
    2693        return self.binary(expression, "<>")
    2694
    2695    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    2696        return self.binary(expression, "IS NOT DISTINCT FROM")
    2697
    2698    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    2699        return self.binary(expression, "IS DISTINCT FROM")
    2700
    2701    def or_sql(self, expression: exp.Or) -> str:
    2702        return self.connector_sql(expression, "OR")
    2703
    2704    def slice_sql(self, expression: exp.Slice) -> str:
    2705        return self.binary(expression, ":")
    2706
    2707    def sub_sql(self, expression: exp.Sub) -> str:
    2708        return self.binary(expression, "-")
    2709
    2710    def trycast_sql(self, expression: exp.TryCast) -> str:
    2711        return self.cast_sql(expression, safe_prefix="TRY_")
    2712
    2713    def log_sql(self, expression: exp.Log) -> str:
    2714        this = expression.this
    2715        expr = expression.expression
    2716
    2717        if not self.dialect.LOG_BASE_FIRST:
    2718            this, expr = expr, this
    2719
    2720        return self.func("LOG", this, expr)
    2721
    2722    def use_sql(self, expression: exp.Use) -> str:
    2723        kind = self.sql(expression, "kind")
    2724        kind = f" {kind}" if kind else ""
    2725        this = self.sql(expression, "this")
    2726        this = f" {this}" if this else ""
    2727        return f"USE{kind}{this}"
    2728
    2729    def binary(self, expression: exp.Binary, op: str) -> str:
    2730        op = self.maybe_comment(op, comments=expression.comments)
    2731        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
    2732
    2733    def function_fallback_sql(self, expression: exp.Func) -> str:
    2734        args = []
    2735
    2736        for key in expression.arg_types:
    2737            arg_value = expression.args.get(key)
    2738
    2739            if isinstance(arg_value, list):
    2740                for value in arg_value:
    2741                    args.append(value)
    2742            elif arg_value is not None:
    2743                args.append(arg_value)
    2744
    2745        if self.normalize_functions:
    2746            name = expression.sql_name()
    2747        else:
    2748            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    2749
    2750        return self.func(name, *args)
    2751
    2752    def func(
    2753        self,
    2754        name: str,
    2755        *args: t.Optional[exp.Expression | str],
    2756        prefix: str = "(",
    2757        suffix: str = ")",
    2758    ) -> str:
    2759        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
    2760
    2761    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    2762        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    2763        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
    2764            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    2765        return ", ".join(arg_sqls)
    2766
    2767    def text_width(self, args: t.Iterable) -> int:
    2768        return sum(len(arg) for arg in args)
    2769
    2770    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2771        return format_time(
    2772            self.sql(expression, "format"),
    2773            self.dialect.INVERSE_TIME_MAPPING,
    2774            self.dialect.INVERSE_TIME_TRIE,
    2775        )
    2776
    2777    def expressions(
    2778        self,
    2779        expression: t.Optional[exp.Expression] = None,
    2780        key: t.Optional[str] = None,
    2781        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    2782        flat: bool = False,
    2783        indent: bool = True,
    2784        skip_first: bool = False,
    2785        sep: str = ", ",
    2786        prefix: str = "",
    2787    ) -> str:
    2788        expressions = expression.args.get(key or "expressions") if expression else sqls
    2789
    2790        if not expressions:
    2791            return ""
    2792
    2793        if flat:
    2794            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    2795
    2796        num_sqls = len(expressions)
    2797
    2798        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2799        pad = " " * self.pad
    2800        stripped_sep = sep.strip()
    2801
    2802        result_sqls = []
    2803        for i, e in enumerate(expressions):
    2804            sql = self.sql(e, comment=False)
    2805            if not sql:
    2806                continue
    2807
    2808            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2809
    2810            if self.pretty:
    2811                if self.leading_comma:
    2812                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2813                else:
    2814                    result_sqls.append(
    2815                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2816                    )
    2817            else:
    2818                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2819
    2820        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2821        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
    2822
    2823    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2824        flat = flat or isinstance(expression.parent, exp.Properties)
    2825        expressions_sql = self.expressions(expression, flat=flat)
    2826        if flat:
    2827            return f"{op} {expressions_sql}"
    2828        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    2829
    2830    def naked_property(self, expression: exp.Property) -> str:
    2831        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2832        if not property_name:
    2833            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2834        return f"{property_name} {self.sql(expression, 'this')}"
    2835
    2836    def set_operation(self, expression: exp.Union, op: str) -> str:
    2837        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
    2838        op = self.seg(op)
    2839        return self.query_modifiers(
    2840            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2841        )
    2842
    2843    def tag_sql(self, expression: exp.Tag) -> str:
    2844        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    2845
    2846    def token_sql(self, token_type: TokenType) -> str:
    2847        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    2848
    2849    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2850        this = self.sql(expression, "this")
    2851        expressions = self.no_identify(self.expressions, expression)
    2852        expressions = (
    2853            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2854        )
    2855        return f"{this}{expressions}"
    2856
    2857    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2858        this = self.sql(expression, "this")
    2859        expressions = self.expressions(expression, flat=True)
    2860        return f"{this}({expressions})"
    2861
    2862    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    2863        return self.binary(expression, "=>")
    2864
    2865    def when_sql(self, expression: exp.When) -> str:
    2866        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2867        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2868        condition = self.sql(expression, "condition")
    2869        condition = f" AND {condition}" if condition else ""
    2870
    2871        then_expression = expression.args.get("then")
    2872        if isinstance(then_expression, exp.Insert):
    2873            then = f"INSERT {self.sql(then_expression, 'this')}"
    2874            if "expression" in then_expression.args:
    2875                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2876        elif isinstance(then_expression, exp.Update):
    2877            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2878                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2879            else:
    2880                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2881        else:
    2882            then = self.sql(then_expression)
    2883        return f"WHEN {matched}{source}{condition} THEN {then}"
    2884
    2885    def merge_sql(self, expression: exp.Merge) -> str:
    2886        table = expression.this
    2887        table_alias = ""
    2888
    2889        hints = table.args.get("hints")
    2890        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    2891            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    2892            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    2893
    2894        this = self.sql(table)
    2895        using = f"USING {self.sql(expression, 'using')}"
    2896        on = f"ON {self.sql(expression, 'on')}"
    2897        expressions = self.expressions(expression, sep=" ")
    2898
    2899        return self.prepend_ctes(
    2900            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
    2901        )
    2902
    2903    def tochar_sql(self, expression: exp.ToChar) -> str:
    2904        if expression.args.get("format"):
    2905            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2906
    2907        return self.sql(exp.cast(expression.this, "text"))
    2908
    2909    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2910        this = self.sql(expression, "this")
    2911        kind = self.sql(expression, "kind")
    2912        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2913        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2914        return f"{this}({kind}{args})"
    2915
    2916    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2917        this = self.sql(expression, "this")
    2918        max = self.sql(expression, "max")
    2919        min = self.sql(expression, "min")
    2920        return f"{this}(MIN {min} MAX {max})"
    2921
    2922    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    2923        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    2924
    2925    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    2926        return ""
    2927
    2928    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    2929        expressions = self.expressions(expression, key="expressions", flat=True)
    2930        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    2931        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    2932        buckets = self.sql(expression, "buckets")
    2933        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
    2934
    2935    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    2936        this = self.sql(expression, "this")
    2937        having = self.sql(expression, "having")
    2938
    2939        if having:
    2940            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    2941
    2942        return self.func("ANY_VALUE", this)
    2943
    2944    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    2945        transform = self.func("TRANSFORM", *expression.expressions)
    2946        row_format_before = self.sql(expression, "row_format_before")
    2947        row_format_before = f" {row_format_before}" if row_format_before else ""
    2948        record_writer = self.sql(expression, "record_writer")
    2949        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    2950        using = f" USING {self.sql(expression, 'command_script')}"
    2951        schema = self.sql(expression, "schema")
    2952        schema = f" AS {schema}" if schema else ""
    2953        row_format_after = self.sql(expression, "row_format_after")
    2954        row_format_after = f" {row_format_after}" if row_format_after else ""
    2955        record_reader = self.sql(expression, "record_reader")
    2956        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    2957        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
    2958
    2959    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    2960        key_block_size = self.sql(expression, "key_block_size")
    2961        if key_block_size:
    2962            return f"KEY_BLOCK_SIZE = {key_block_size}"
    2963
    2964        using = self.sql(expression, "using")
    2965        if using:
    2966            return f"USING {using}"
    2967
    2968        parser = self.sql(expression, "parser")
    2969        if parser:
    2970            return f"WITH PARSER {parser}"
    2971
    2972        comment = self.sql(expression, "comment")
    2973        if comment:
    2974            return f"COMMENT {comment}"
    2975
    2976        visible = expression.args.get("visible")
    2977        if visible is not None:
    2978            return "VISIBLE" if visible else "INVISIBLE"
    2979
    2980        engine_attr = self.sql(expression, "engine_attr")
    2981        if engine_attr:
    2982            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    2983
    2984        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    2985        if secondary_engine_attr:
    2986            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    2987
    2988        self.unsupported("Unsupported index constraint option.")
    2989        return ""
    2990
    2991    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    2992        kind = self.sql(expression, "kind")
    2993        kind = f"{kind} INDEX" if kind else "INDEX"
    2994        this = self.sql(expression, "this")
    2995        this = f" {this}" if this else ""
    2996        index_type = self.sql(expression, "index_type")
    2997        index_type = f" USING {index_type}" if index_type else ""
    2998        schema = self.sql(expression, "schema")
    2999        schema = f" {schema}" if schema else ""
    3000        options = self.expressions(expression, key="options", sep=" ")
    3001        options = f" {options}" if options else ""
    3002        return f"{kind}{this}{index_type}{schema}{options}"
    3003
    3004    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3005        if self.NVL2_SUPPORTED:
    3006            return self.function_fallback_sql(expression)
    3007
    3008        case = exp.Case().when(
    3009            expression.this.is_(exp.null()).not_(copy=False),
    3010            expression.args["true"],
    3011            copy=False,
    3012        )
    3013        else_cond = expression.args.get("false")
    3014        if else_cond:
    3015            case.else_(else_cond, copy=False)
    3016
    3017        return self.sql(case)
    3018
    3019    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3020        this = self.sql(expression, "this")
    3021        expr = self.sql(expression, "expression")
    3022        iterator = self.sql(expression, "iterator")
    3023        condition = self.sql(expression, "condition")
    3024        condition = f" IF {condition}" if condition else ""
    3025        return f"{this} FOR {expr} IN {iterator}{condition}"
    3026
    3027    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    3028        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
    3029
    3030    def opclass_sql(self, expression: exp.Opclass) -> str:
    3031        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    3032
    3033    def predict_sql(self, expression: exp.Predict) -> str:
    3034        model = self.sql(expression, "this")
    3035        model = f"MODEL {model}"
    3036        table = self.sql(expression, "expression")
    3037        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3038        parameters = self.sql(expression, "params_struct")
    3039        return self.func("PREDICT", model, table, parameters or None)
    3040
    3041    def forin_sql(self, expression: exp.ForIn) -> str:
    3042        this = self.sql(expression, "this")
    3043        expression_sql = self.sql(expression, "expression")
    3044        return f"FOR {this} DO {expression_sql}"
    3045
    3046    def refresh_sql(self, expression: exp.Refresh) -> str:
    3047        this = self.sql(expression, "this")
    3048        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3049        return f"REFRESH {table}{this}"
    3050
    3051    def operator_sql(self, expression: exp.Operator) -> str:
    3052        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
    3053
    3054    def _simplify_unless_literal(self, expression: E) -> E:
    3055        if not isinstance(expression, exp.Literal):
    3056            from sqlglot.optimizer.simplify import simplify
    3057
    3058            expression = simplify(expression)
    3059
    3060        return expression
    3061
    3062    def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]:
    3063        return [
    3064            exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string(""))
    3065            for value in values
    3066            if value
    3067        ]

Generator converts a given syntax tree to the corresponding SQL string.

###### Arguments:

- pretty: Whether or not to format the produced SQL string. Default: False.
- identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True or 'always': Always quote. 'safe': Only quote identifiers that are case insensitive.
- normalize: Whether or not to normalize identifiers to lowercase. Default: False.
- pad: Determines the pad size in a formatted string. Default: 2.
- indent: Determines the indentation size in a formatted string. Default: 2.
- normalize_functions: Whether or not to normalize all function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
- unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
- max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
- leading_comma: Determines whether or not the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
- max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
- comments: Whether or not to preserve comments in the output SQL code. Default: True

Generator( pretty: Optional[bool] = None, identify: str | bool = False, normalize: bool = False, pad: int = 2, indent: int = 2, normalize_functions: Union[str, bool, NoneType] = None, unsupported_level: sqlglot.errors.ErrorLevel = <ErrorLevel.WARN: 'WARN'>, max_unsupported: int = 3, leading_comma: bool = False, max_text_width: int = 80, comments: bool = True, dialect: Union[str, sqlglot.dialects.dialect.Dialect, Type[sqlglot.dialects.dialect.Dialect], NoneType] = None) View Source

    393    def __init__(
    394        self,
    395        pretty: t.Optional[bool] = None,
    396        identify: str | bool = False,
    397        normalize: bool = False,
    398        pad: int = 2,
    399        indent: int = 2,
    400        normalize_functions: t.Optional[str | bool] = None,
    401        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    402        max_unsupported: int = 3,
    403        leading_comma: bool = False,
    404        max_text_width: int = 80,
    405        comments: bool = True,
    406        dialect: DialectType = None,
    407    ):
    408        import sqlglot
    409        from sqlglot.dialects import Dialect
    410
    411        self.pretty = pretty if pretty is not None else sqlglot.pretty
    412        self.identify = identify
    413        self.normalize = normalize
    414        self.pad = pad
    415        self._indent = indent
    416        self.unsupported_level = unsupported_level
    417        self.max_unsupported = max_unsupported
    418        self.leading_comma = leading_comma
    419        self.max_text_width = max_text_width
    420        self.comments = comments
    421        self.dialect = Dialect.get_or_raise(dialect)
    422
    423        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    424        self.normalize_functions = (
    425            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    426        )
    427
    428        self.unsupported_messages: t.List[str] = []
    429        self._escaped_quote_end: str = (
    430            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    431        )
    432        self._escaped_identifier_end: str = (
    433            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
    434        )

TRANSFORMS = {<class 'sqlglot.expressions.DateAdd'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CheckColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED = True
LOCKING_READS_SUPPORTED = False
EXPLICIT_UNION = False
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SIZE_IS_PERCENT = False
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
COLUMN_JOIN_MARKS_SUPPORTED = False
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
TYPE_MAPPING = {<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET'}
STAR_MAPPING = {'except': 'EXCEPT', 'replace': 'REPLACE'}
TIME_PART_SINGULARS = {'microseconds': 'microsecond', 'seconds': 'second', 'minutes': 'minute', 'hours': 'hour', 'days': 'day', 'weeks': 'week', 'months': 'month', 'quarters': 'quarter', 'years': 'year'}
TOKEN_MAPPING: Dict[sqlglot.token_type.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
KEY_VALUE_DEFINITONS = (<class 'sqlglot.expressions.Bracket'>, <class 'sqlglot.expressions.EQ'>, <class 'sqlglot.expressions.PropertyEQ'>, <class 'sqlglot.expressions.Slice'>)
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str: View Source

    436    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
    437        """
    438        Generates the SQL string corresponding to the given syntax tree.
    439
    440        Args:
    441            expression: The syntax tree.
    442            copy: Whether or not to copy the expression. The generator performs mutations so
    443                it is safer to copy.
    444
    445        Returns:
    446            The SQL string corresponding to `expression`.
    447        """
    448        if copy:
    449            expression = expression.copy()
    450
    451        expression = self.preprocess(expression)
    452
    453        self.unsupported_messages = []
    454        sql = self.sql(expression).strip()
    455
    456        if self.pretty:
    457            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    458
    459        if self.unsupported_level == ErrorLevel.IGNORE:
    460            return sql
    461
    462        if self.unsupported_level == ErrorLevel.WARN:
    463            for msg in self.unsupported_messages:
    464                logger.warning(msg)
    465        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    466            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    467
    468        return sql

Generates the SQL string corresponding to the given syntax tree.

###### Arguments:

- expression: The syntax tree.
- copy: Whether or not to copy the expression. The generator performs mutations so it is safer to copy.

###### Returns:

> The SQL string corresponding to `expression`.

def preprocess( self, expression: sqlglot.expressions.Expression) -> sqlglot.expressions.Expression: View Source

    470    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    471        """Apply generic preprocessing transformations to a given expression."""
    472        if (
    473            not expression.parent
    474            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
    475            and any(node.parent is not expression for node in expression.find_all(exp.With))
    476        ):
    477            from sqlglot.transforms import move_ctes_to_top_level
    478
    479            expression = move_ctes_to_top_level(expression)
    480
    481        if self.ENSURE_BOOLS:
    482            from sqlglot.transforms import ensure_bools
    483
    484            expression = ensure_bools(expression)
    485
    486        return expression

Apply generic preprocessing transformations to a given expression.

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

    488    def unsupported(self, message: str) -> None:
    489        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    490            raise UnsupportedError(message)
    491        self.unsupported_messages.append(message)

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

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

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

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

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

    499    def pad_comment(self, comment: str) -> str:
    500        comment = " " + comment if comment[0].strip() else comment
    501        comment = comment + " " if comment[-1].strip() else comment
    502        return comment

def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None) -> str: View Source

    504    def maybe_comment(
    505        self,
    506        sql: str,
    507        expression: t.Optional[exp.Expression] = None,
    508        comments: t.Optional[t.List[str]] = None,
    509    ) -> str:
    510        comments = (
    511            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    512            if self.comments
    513            else None
    514        )
    515
    516        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    517            return sql
    518
    519        comments_sql = " ".join(
    520            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    521        )
    522
    523        if not comments_sql:
    524            return sql
    525
    526        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    527            return (
    528                f"{self.sep()}{comments_sql}{sql}"
    529                if sql[0].isspace()
    530                else f"{comments_sql}{self.sep()}{sql}"
    531            )
    532
    533        return f"{sql} {comments_sql}"

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

    535    def wrap(self, expression: exp.Expression | str) -> str:
    536        this_sql = self.indent(
    537            self.sql(expression)
    538            if isinstance(expression, (exp.Select, exp.Union))
    539            else self.sql(expression, "this"),
    540            level=1,
    541            pad=0,
    542        )
    543        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    545    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    546        original = self.identify
    547        self.identify = False
    548        result = func(*args, **kwargs)
    549        self.identify = original
    550        return result

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

    552    def normalize_func(self, name: str) -> str:
    553        if self.normalize_functions == "upper" or self.normalize_functions is True:
    554            return name.upper()
    555        if self.normalize_functions == "lower":
    556            return name.lower()
    557        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

    559    def indent(
    560        self,
    561        sql: str,
    562        level: int = 0,
    563        pad: t.Optional[int] = None,
    564        skip_first: bool = False,
    565        skip_last: bool = False,
    566    ) -> str:
    567        if not self.pretty:
    568            return sql
    569
    570        pad = self.pad if pad is None else pad
    571        lines = sql.split("\n")
    572
    573        return "\n".join(
    574            line
    575            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    576            else f"{' ' * (level * self._indent + pad)}{line}"
    577            for i, line in enumerate(lines)
    578        )

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

    580    def sql(
    581        self,
    582        expression: t.Optional[str | exp.Expression],
    583        key: t.Optional[str] = None,
    584        comment: bool = True,
    585    ) -> str:
    586        if not expression:
    587            return ""
    588
    589        if isinstance(expression, str):
    590            return expression
    591
    592        if key:
    593            value = expression.args.get(key)
    594            if value:
    595                return self.sql(value)
    596            return ""
    597
    598        transform = self.TRANSFORMS.get(expression.__class__)
    599
    600        if callable(transform):
    601            sql = transform(self, expression)
    602        elif transform:
    603            sql = transform
    604        elif isinstance(expression, exp.Expression):
    605            exp_handler_name = f"{expression.key}_sql"
    606
    607            if hasattr(self, exp_handler_name):
    608                sql = getattr(self, exp_handler_name)(expression)
    609            elif isinstance(expression, exp.Func):
    610                sql = self.function_fallback_sql(expression)
    611            elif isinstance(expression, exp.Property):
    612                sql = self.property_sql(expression)
    613            else:
    614                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    615        else:
    616            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    617
    618        return self.maybe_comment(sql, expression) if self.comments and comment else sql

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

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

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

    625    def cache_sql(self, expression: exp.Cache) -> str:
    626        lazy = " LAZY" if expression.args.get("lazy") else ""
    627        table = self.sql(expression, "this")
    628        options = expression.args.get("options")
    629        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    630        sql = self.sql(expression, "expression")
    631        sql = f" AS{self.sep()}{sql}" if sql else ""
    632        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    633        return self.prepend_ctes(expression, sql)

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

    635    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    636        if isinstance(expression.parent, exp.Cast):
    637            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    638        default = "DEFAULT " if expression.args.get("default") else ""
    639        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    641    def column_sql(self, expression: exp.Column) -> str:
    642        join_mark = " (+)" if expression.args.get("join_mark") else ""
    643
    644        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
    645            join_mark = ""
    646            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    647
    648        column = ".".join(
    649            self.sql(part)
    650            for part in (
    651                expression.args.get("catalog"),
    652                expression.args.get("db"),
    653                expression.args.get("table"),
    654                expression.args.get("this"),
    655            )
    656            if part
    657        )
    658
    659        return f"{column}{join_mark}"

def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str: View Source

    661    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    662        this = self.sql(expression, "this")
    663        this = f" {this}" if this else ""
    664        position = self.sql(expression, "position")
    665        return f"{position}{this}"

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

    667    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    668        column = self.sql(expression, "this")
    669        kind = self.sql(expression, "kind")
    670        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    671        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    672        kind = f"{sep}{kind}" if kind else ""
    673        constraints = f" {constraints}" if constraints else ""
    674        position = self.sql(expression, "position")
    675        position = f" {position}" if position else ""
    676
    677        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    678            kind = ""
    679
    680        return f"{exists}{column}{kind}{constraints}{position}"

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

    682    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    683        this = self.sql(expression, "this")
    684        kind_sql = self.sql(expression, "kind").strip()
    685        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str: View Source

    687    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    688        this = self.sql(expression, "this")
    689        if expression.args.get("not_null"):
    690            persisted = " PERSISTED NOT NULL"
    691        elif expression.args.get("persisted"):
    692            persisted = " PERSISTED"
    693        else:
    694            persisted = ""
    695        return f"AS {this}{persisted}"

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

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

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

    700    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    701        if isinstance(expression.this, list):
    702            this = self.wrap(self.expressions(expression, key="this", flat=True))
    703        else:
    704            this = self.sql(expression, "this")
    705
    706        return f"COMPRESS {this}"

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

    708    def generatedasidentitycolumnconstraint_sql(
    709        self, expression: exp.GeneratedAsIdentityColumnConstraint
    710    ) -> str:
    711        this = ""
    712        if expression.this is not None:
    713            on_null = " ON NULL" if expression.args.get("on_null") else ""
    714            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    715
    716        start = expression.args.get("start")
    717        start = f"START WITH {start}" if start else ""
    718        increment = expression.args.get("increment")
    719        increment = f" INCREMENT BY {increment}" if increment else ""
    720        minvalue = expression.args.get("minvalue")
    721        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    722        maxvalue = expression.args.get("maxvalue")
    723        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    724        cycle = expression.args.get("cycle")
    725        cycle_sql = ""
    726
    727        if cycle is not None:
    728            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    729            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    730
    731        sequence_opts = ""
    732        if start or increment or cycle_sql:
    733            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    734            sequence_opts = f" ({sequence_opts.strip()})"
    735
    736        expr = self.sql(expression, "expression")
    737        expr = f"({expr})" if expr else "IDENTITY"
    738
    739        return f"GENERATED{this} AS {expr}{sequence_opts}"

def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str: View Source

    741    def generatedasrowcolumnconstraint_sql(
    742        self, expression: exp.GeneratedAsRowColumnConstraint
    743    ) -> str:
    744        start = "START" if expression.args["start"] else "END"
    745        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    746        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str: View Source

    748    def periodforsystemtimeconstraint_sql(
    749        self, expression: exp.PeriodForSystemTimeConstraint
    750    ) -> str:
    751        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"

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

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

def transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str: View Source

    756    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
    757        return f"AS {self.sql(expression, 'this')}"

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

    759    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    760        desc = expression.args.get("desc")
    761        if desc is not None:
    762            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    763        return f"PRIMARY KEY"

def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str: View Source

    765    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    766        this = self.sql(expression, "this")
    767        this = f" {this}" if this else ""
    768        index_type = expression.args.get("index_type")
    769        index_type = f" USING {index_type}" if index_type else ""
    770        return f"UNIQUE{this}{index_type}"

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

    772    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
    773        return self.sql(expression, "this")

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

    775    def create_sql(self, expression: exp.Create) -> str:
    776        kind = self.sql(expression, "kind").upper()
    777        properties = expression.args.get("properties")
    778        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    779
    780        this = self.createable_sql(expression, properties_locs)
    781
    782        properties_sql = ""
    783        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    784            exp.Properties.Location.POST_WITH
    785        ):
    786            properties_sql = self.sql(
    787                exp.Properties(
    788                    expressions=[
    789                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    790                        *properties_locs[exp.Properties.Location.POST_WITH],
    791                    ]
    792                )
    793            )
    794
    795        begin = " BEGIN" if expression.args.get("begin") else ""
    796        end = " END" if expression.args.get("end") else ""
    797
    798        expression_sql = self.sql(expression, "expression")
    799        if expression_sql:
    800            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    801
    802            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    803                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    804                    postalias_props_sql = self.properties(
    805                        exp.Properties(
    806                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    807                        ),
    808                        wrapped=False,
    809                    )
    810                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
    811                else:
    812                    expression_sql = f" AS{expression_sql}"
    813
    814        postindex_props_sql = ""
    815        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    816            postindex_props_sql = self.properties(
    817                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    818                wrapped=False,
    819                prefix=" ",
    820            )
    821
    822        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    823        indexes = f" {indexes}" if indexes else ""
    824        index_sql = indexes + postindex_props_sql
    825
    826        replace = " OR REPLACE" if expression.args.get("replace") else ""
    827        unique = " UNIQUE" if expression.args.get("unique") else ""
    828
    829        postcreate_props_sql = ""
    830        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    831            postcreate_props_sql = self.properties(
    832                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    833                sep=" ",
    834                prefix=" ",
    835                wrapped=False,
    836            )
    837
    838        modifiers = "".join((replace, unique, postcreate_props_sql))
    839
    840        postexpression_props_sql = ""
    841        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    842            postexpression_props_sql = self.properties(
    843                exp.Properties(
    844                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    845                ),
    846                sep=" ",
    847                prefix=" ",
    848                wrapped=False,
    849            )
    850
    851        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    852        no_schema_binding = (
    853            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    854        )
    855
    856        clone = self.sql(expression, "clone")
    857        clone = f" {clone}" if clone else ""
    858
    859        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    860        return self.prepend_ctes(expression, expression_sql)

def clone_sql(self, expression: sqlglot.expressions.Clone) -> str: View Source

    862    def clone_sql(self, expression: exp.Clone) -> str:
    863        this = self.sql(expression, "this")
    864        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    865        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    866        return f"{shallow}{keyword} {this}"

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

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

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

    871    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    872        with_ = self.sql(expression, "with")
    873        if with_:
    874            sql = f"{with_}{self.sep()}{sql}"
    875        return sql

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

    877    def with_sql(self, expression: exp.With) -> str:
    878        sql = self.expressions(expression, flat=True)
    879        recursive = (
    880            "RECURSIVE "
    881            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    882            else ""
    883        )
    884
    885        return f"WITH {recursive}{sql}"

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

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

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

    891    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    892        alias = self.sql(expression, "this")
    893        columns = self.expressions(expression, key="columns", flat=True)
    894        columns = f"({columns})" if columns else ""
    895
    896        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    897            alias = "_t"
    898
    899        return f"{alias}{columns}"

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

    901    def bitstring_sql(self, expression: exp.BitString) -> str:
    902        this = self.sql(expression, "this")
    903        if self.dialect.BIT_START:
    904            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    905        return f"{int(this, 2)}"

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

    907    def hexstring_sql(self, expression: exp.HexString) -> str:
    908        this = self.sql(expression, "this")
    909        if self.dialect.HEX_START:
    910            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    911        return f"{int(this, 16)}"

def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str: View Source

    913    def bytestring_sql(self, expression: exp.ByteString) -> str:
    914        this = self.sql(expression, "this")
    915        if self.dialect.BYTE_START:
    916            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    917        return this

def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str: View Source

    919    def rawstring_sql(self, expression: exp.RawString) -> str:
    920        string = self.escape_str(expression.this.replace("\\", "\\\\"))
    921        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"

def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str: View Source

    923    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    924        this = self.sql(expression, "this")
    925        specifier = self.sql(expression, "expression")
    926        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    927        return f"{this}{specifier}"

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

    929    def datatype_sql(self, expression: exp.DataType) -> str:
    930        type_value = expression.this
    931
    932        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    933            type_sql = self.sql(expression, "kind")
    934        else:
    935            type_sql = (
    936                self.TYPE_MAPPING.get(type_value, type_value.value)
    937                if isinstance(type_value, exp.DataType.Type)
    938                else type_value
    939            )
    940
    941        nested = ""
    942        interior = self.expressions(expression, flat=True)
    943        values = ""
    944
    945        if interior:
    946            if expression.args.get("nested"):
    947                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    948                if expression.args.get("values") is not None:
    949                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    950                    values = self.expressions(expression, key="values", flat=True)
    951                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    952            elif type_value == exp.DataType.Type.INTERVAL:
    953                nested = f" {interior}"
    954            else:
    955                nested = f"({interior})"
    956
    957        type_sql = f"{type_sql}{nested}{values}"
    958        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    959            exp.DataType.Type.TIMETZ,
    960            exp.DataType.Type.TIMESTAMPTZ,
    961        ):
    962            type_sql = f"{type_sql} WITH TIME ZONE"
    963
    964        return type_sql

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

    966    def directory_sql(self, expression: exp.Directory) -> str:
    967        local = "LOCAL " if expression.args.get("local") else ""
    968        row_format = self.sql(expression, "row_format")
    969        row_format = f" {row_format}" if row_format else ""
    970        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    972    def delete_sql(self, expression: exp.Delete) -> str:
    973        this = self.sql(expression, "this")
    974        this = f" FROM {this}" if this else ""
    975        using = self.sql(expression, "using")
    976        using = f" USING {using}" if using else ""
    977        where = self.sql(expression, "where")
    978        returning = self.sql(expression, "returning")
    979        limit = self.sql(expression, "limit")
    980        tables = self.expressions(expression, key="tables")
    981        tables = f" {tables}" if tables else ""
    982        if self.RETURNING_END:
    983            expression_sql = f"{this}{using}{where}{returning}{limit}"
    984        else:
    985            expression_sql = f"{returning}{this}{using}{where}{limit}"
    986        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    988    def drop_sql(self, expression: exp.Drop) -> str:
    989        this = self.sql(expression, "this")
    990        kind = expression.args["kind"]
    991        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    992        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    993        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    994        cascade = " CASCADE" if expression.args.get("cascade") else ""
    995        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    996        purge = " PURGE" if expression.args.get("purge") else ""
    997        return (
    998            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
    999        )

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

    1001    def except_sql(self, expression: exp.Except) -> str:
    1002        return self.prepend_ctes(
    1003            expression,
    1004            self.set_operation(expression, self.except_op(expression)),
    1005        )

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

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

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

    1010    def fetch_sql(self, expression: exp.Fetch) -> str:
    1011        direction = expression.args.get("direction")
    1012        direction = f" {direction.upper()}" if direction else ""
    1013        count = expression.args.get("count")
    1014        count = f" {count}" if count else ""
    1015        if expression.args.get("percent"):
    1016            count = f"{count} PERCENT"
    1017        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1018        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1020    def filter_sql(self, expression: exp.Filter) -> str:
    1021        if self.AGGREGATE_FILTER_SUPPORTED:
    1022            this = self.sql(expression, "this")
    1023            where = self.sql(expression, "expression").strip()
    1024            return f"{this} FILTER({where})"
    1025
    1026        agg = expression.this
    1027        agg_arg = agg.this
    1028        cond = expression.expression.this
    1029        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1030        return self.sql(agg)

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

    1032    def hint_sql(self, expression: exp.Hint) -> str:
    1033        if not self.QUERY_HINTS:
    1034            self.unsupported("Hints are not supported")
    1035            return ""
    1036
    1037        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1039    def index_sql(self, expression: exp.Index) -> str:
    1040        unique = "UNIQUE " if expression.args.get("unique") else ""
    1041        primary = "PRIMARY " if expression.args.get("primary") else ""
    1042        amp = "AMP " if expression.args.get("amp") else ""
    1043        name = self.sql(expression, "this")
    1044        name = f"{name} " if name else ""
    1045        table = self.sql(expression, "table")
    1046        table = f"{self.INDEX_ON} {table}" if table else ""
    1047        using = self.sql(expression, "using")
    1048        using = f" USING {using}" if using else ""
    1049        index = "INDEX " if not table else ""
    1050        columns = self.expressions(expression, key="columns", flat=True)
    1051        columns = f"({columns})" if columns else ""
    1052        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1053        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1054        where = self.sql(expression, "where")
    1055        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"

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

    1057    def identifier_sql(self, expression: exp.Identifier) -> str:
    1058        text = expression.name
    1059        lower = text.lower()
    1060        text = lower if self.normalize and not expression.quoted else text
    1061        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1062        if (
    1063            expression.quoted
    1064            or self.dialect.can_identify(text, self.identify)
    1065            or lower in self.RESERVED_KEYWORDS
    1066            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1067        ):
    1068            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1069        return text

def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str: View Source

    1071    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1072        input_format = self.sql(expression, "input_format")
    1073        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1074        output_format = self.sql(expression, "output_format")
    1075        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1076        return self.sep().join((input_format, output_format))

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

    1078    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1079        string = self.sql(exp.Literal.string(expression.name))
    1080        return f"{prefix}{string}"

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

    1082    def partition_sql(self, expression: exp.Partition) -> str:
    1083        return f"PARTITION({self.expressions(expression, flat=True)})"

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

    1085    def properties_sql(self, expression: exp.Properties) -> str:
    1086        root_properties = []
    1087        with_properties = []
    1088
    1089        for p in expression.expressions:
    1090            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1091            if p_loc == exp.Properties.Location.POST_WITH:
    1092                with_properties.append(p)
    1093            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1094                root_properties.append(p)
    1095
    1096        return self.root_properties(
    1097            exp.Properties(expressions=root_properties)
    1098        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    1100    def root_properties(self, properties: exp.Properties) -> str:
    1101        if properties.expressions:
    1102            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1103        return ""

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

    1105    def properties(
    1106        self,
    1107        properties: exp.Properties,
    1108        prefix: str = "",
    1109        sep: str = ", ",
    1110        suffix: str = "",
    1111        wrapped: bool = True,
    1112    ) -> str:
    1113        if properties.expressions:
    1114            expressions = self.expressions(properties, sep=sep, indent=False)
    1115            if expressions:
    1116                expressions = self.wrap(expressions) if wrapped else expressions
    1117                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1118        return ""

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

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

def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict: View Source

    1123    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1124        properties_locs = defaultdict(list)
    1125        for p in properties.expressions:
    1126            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1127            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1128                properties_locs[p_loc].append(p)
    1129            else:
    1130                self.unsupported(f"Unsupported property {p.key}")
    1131
    1132        return properties_locs

def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str: View Source

    1134    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1135        if isinstance(expression.this, exp.Dot):
    1136            return self.sql(expression, "this")
    1137        return f"'{expression.name}'" if string_key else expression.name

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

    1139    def property_sql(self, expression: exp.Property) -> str:
    1140        property_cls = expression.__class__
    1141        if property_cls == exp.Property:
    1142            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1143
    1144        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1145        if not property_name:
    1146            self.unsupported(f"Unsupported property {expression.key}")
    1147
    1148        return f"{property_name}={self.sql(expression, 'this')}"

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

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

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

    1155    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1156        no = "NO " if expression.args.get("no") else ""
    1157        protection = " PROTECTION" if expression.args.get("protection") else ""
    1158        return f"{no}FALLBACK{protection}"

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

    1160    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1161        no = "NO " if expression.args.get("no") else ""
    1162        local = expression.args.get("local")
    1163        local = f"{local} " if local else ""
    1164        dual = "DUAL " if expression.args.get("dual") else ""
    1165        before = "BEFORE " if expression.args.get("before") else ""
    1166        after = "AFTER " if expression.args.get("after") else ""
    1167        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1169    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1170        freespace = self.sql(expression, "this")
    1171        percent = " PERCENT" if expression.args.get("percent") else ""
    1172        return f"FREESPACE={freespace}{percent}"

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

    1174    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1175        if expression.args.get("default"):
    1176            property = "DEFAULT"
    1177        elif expression.args.get("on"):
    1178            property = "ON"
    1179        else:
    1180            property = "OFF"
    1181        return f"CHECKSUM={property}"

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

    1183    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1184        if expression.args.get("no"):
    1185            return "NO MERGEBLOCKRATIO"
    1186        if expression.args.get("default"):
    1187            return "DEFAULT MERGEBLOCKRATIO"
    1188
    1189        percent = " PERCENT" if expression.args.get("percent") else ""
    1190        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1192    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1193        default = expression.args.get("default")
    1194        minimum = expression.args.get("minimum")
    1195        maximum = expression.args.get("maximum")
    1196        if default or minimum or maximum:
    1197            if default:
    1198                prop = "DEFAULT"
    1199            elif minimum:
    1200                prop = "MINIMUM"
    1201            else:
    1202                prop = "MAXIMUM"
    1203            return f"{prop} DATABLOCKSIZE"
    1204        units = expression.args.get("units")
    1205        units = f" {units}" if units else ""
    1206        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1208    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1209        autotemp = expression.args.get("autotemp")
    1210        always = expression.args.get("always")
    1211        default = expression.args.get("default")
    1212        manual = expression.args.get("manual")
    1213        never = expression.args.get("never")
    1214
    1215        if autotemp is not None:
    1216            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1217        elif always:
    1218            prop = "ALWAYS"
    1219        elif default:
    1220            prop = "DEFAULT"
    1221        elif manual:
    1222            prop = "MANUAL"
    1223        elif never:
    1224            prop = "NEVER"
    1225        return f"BLOCKCOMPRESSION={prop}"

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

    1227    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1228        no = expression.args.get("no")
    1229        no = " NO" if no else ""
    1230        concurrent = expression.args.get("concurrent")
    1231        concurrent = " CONCURRENT" if concurrent else ""
    1232
    1233        for_ = ""
    1234        if expression.args.get("for_all"):
    1235            for_ = " FOR ALL"
    1236        elif expression.args.get("for_insert"):
    1237            for_ = " FOR INSERT"
    1238        elif expression.args.get("for_none"):
    1239            for_ = " FOR NONE"
    1240        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str: View Source

    1242    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1243        if isinstance(expression.this, list):
    1244            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1245        if expression.this:
    1246            modulus = self.sql(expression, "this")
    1247            remainder = self.sql(expression, "expression")
    1248            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1249
    1250        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1251        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1252        return f"FROM ({from_expressions}) TO ({to_expressions})"

def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str: View Source

    1254    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1255        this = self.sql(expression, "this")
    1256
    1257        for_values_or_default = expression.expression
    1258        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1259            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1260        else:
    1261            for_values_or_default = " DEFAULT"
    1262
    1263        return f"PARTITION OF {this}{for_values_or_default}"

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

    1265    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1266        kind = expression.args.get("kind")
    1267        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1268        for_or_in = expression.args.get("for_or_in")
    1269        for_or_in = f" {for_or_in}" if for_or_in else ""
    1270        lock_type = expression.args.get("lock_type")
    1271        override = " OVERRIDE" if expression.args.get("override") else ""
    1272        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1274    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1275        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1276        statistics = expression.args.get("statistics")
    1277        statistics_sql = ""
    1278        if statistics is not None:
    1279            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1280        return f"{data_sql}{statistics_sql}"

def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str: View Source

    1282    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1283        sql = "WITH(SYSTEM_VERSIONING=ON"
    1284
    1285        if expression.this:
    1286            history_table = self.sql(expression, "this")
    1287            sql = f"{sql}(HISTORY_TABLE={history_table}"
    1288
    1289            if expression.expression:
    1290                data_consistency_check = self.sql(expression, "expression")
    1291                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
    1292
    1293            sql = f"{sql})"
    1294
    1295        return f"{sql})"

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

    1297    def insert_sql(self, expression: exp.Insert) -> str:
    1298        overwrite = expression.args.get("overwrite")
    1299
    1300        if isinstance(expression.this, exp.Directory):
    1301            this = " OVERWRITE" if overwrite else " INTO"
    1302        else:
    1303            this = " OVERWRITE TABLE" if overwrite else " INTO"
    1304
    1305        alternative = expression.args.get("alternative")
    1306        alternative = f" OR {alternative}" if alternative else ""
    1307        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1308
    1309        this = f"{this} {self.sql(expression, 'this')}"
    1310
    1311        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1312        partition_sql = (
    1313            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1314        )
    1315        where = self.sql(expression, "where")
    1316        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1317        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1318        conflict = self.sql(expression, "conflict")
    1319        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1320        returning = self.sql(expression, "returning")
    1321
    1322        if self.RETURNING_END:
    1323            expression_sql = f"{expression_sql}{conflict}{returning}"
    1324        else:
    1325            expression_sql = f"{returning}{expression_sql}{conflict}"
    1326
    1327        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1328        return self.prepend_ctes(expression, sql)

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

    1330    def intersect_sql(self, expression: exp.Intersect) -> str:
    1331        return self.prepend_ctes(
    1332            expression,
    1333            self.set_operation(expression, self.intersect_op(expression)),
    1334        )

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

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

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

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

def kill_sql(self, expression: sqlglot.expressions.Kill) -> str: View Source

    1342    def kill_sql(self, expression: exp.Kill) -> str:
    1343        kind = self.sql(expression, "kind")
    1344        kind = f" {kind}" if kind else ""
    1345        this = self.sql(expression, "this")
    1346        this = f" {this}" if this else ""
    1347        return f"KILL{kind}{this}"

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

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

def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str: View Source

    1352    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1353        return expression.name.upper()

def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str: View Source

    1355    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1356        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1357        constraint = self.sql(expression, "constraint")
    1358        if constraint:
    1359            constraint = f"ON CONSTRAINT {constraint}"
    1360        key = self.expressions(expression, key="key", flat=True)
    1361        do = "" if expression.args.get("duplicate") else " DO "
    1362        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1363        expressions = self.expressions(expression, flat=True)
    1364        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1365        if expressions:
    1366            expressions = f"UPDATE {set_keyword}{expressions}"
    1367        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"

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

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

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

    1372    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1373        fields = expression.args.get("fields")
    1374        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1375        escaped = expression.args.get("escaped")
    1376        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1377        items = expression.args.get("collection_items")
    1378        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1379        keys = expression.args.get("map_keys")
    1380        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1381        lines = expression.args.get("lines")
    1382        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1383        null = expression.args.get("null")
    1384        null = f" NULL DEFINED AS {null}" if null else ""
    1385        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str: View Source

    1387    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1388        return f"WITH ({self.expressions(expression, flat=True)})"

def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str: View Source

    1390    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1391        this = f"{self.sql(expression, 'this')} INDEX"
    1392        target = self.sql(expression, "target")
    1393        target = f" FOR {target}" if target else ""
    1394        return f"{this}{target} ({self.expressions(expression, flat=True)})"

def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str: View Source

    1396    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1397        this = self.sql(expression, "this")
    1398        kind = self.sql(expression, "kind")
    1399        expr = self.sql(expression, "expression")
    1400        return f"{this} ({kind} => {expr})"

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

    1402    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1403        table = ".".join(
    1404            self.sql(part)
    1405            for part in (
    1406                expression.args.get("catalog"),
    1407                expression.args.get("db"),
    1408                expression.args.get("this"),
    1409            )
    1410            if part is not None
    1411        )
    1412
    1413        version = self.sql(expression, "version")
    1414        version = f" {version}" if version else ""
    1415        alias = self.sql(expression, "alias")
    1416        alias = f"{sep}{alias}" if alias else ""
    1417        hints = self.expressions(expression, key="hints", sep=" ")
    1418        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1419        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1420        pivots = f" {pivots}" if pivots else ""
    1421        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
    1422        laterals = self.expressions(expression, key="laterals", sep="")
    1423
    1424        file_format = self.sql(expression, "format")
    1425        if file_format:
    1426            pattern = self.sql(expression, "pattern")
    1427            pattern = f", PATTERN => {pattern}" if pattern else ""
    1428            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1429
    1430        index = self.sql(expression, "index")
    1431        index = f" AT {index}" if index else ""
    1432
    1433        ordinality = expression.args.get("ordinality") or ""
    1434        if ordinality:
    1435            ordinality = f" WITH ORDINALITY{alias}"
    1436            alias = ""
    1437
    1438        when = self.sql(expression, "when")
    1439        if when:
    1440            table = f"{table} {when}"
    1441
    1442        return f"{table}{version}{file_format}{alias}{index}{hints}{pivots}{joins}{laterals}{ordinality}"

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

    1444    def tablesample_sql(
    1445        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1446    ) -> str:
    1447        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1448            table = expression.this.copy()
    1449            table.set("alias", None)
    1450            this = self.sql(table)
    1451            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1452        else:
    1453            this = self.sql(expression, "this")
    1454            alias = ""
    1455
    1456        method = self.sql(expression, "method")
    1457        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1458        numerator = self.sql(expression, "bucket_numerator")
    1459        denominator = self.sql(expression, "bucket_denominator")
    1460        field = self.sql(expression, "bucket_field")
    1461        field = f" ON {field}" if field else ""
    1462        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1463        percent = self.sql(expression, "percent")
    1464        percent = f"{percent} PERCENT" if percent else ""
    1465        rows = self.sql(expression, "rows")
    1466        rows = f"{rows} ROWS" if rows else ""
    1467
    1468        size = self.sql(expression, "size")
    1469        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1470            size = f"{size} PERCENT"
    1471
    1472        seed = self.sql(expression, "seed")
    1473        seed = f" {seed_prefix} ({seed})" if seed else ""
    1474        kind = expression.args.get("kind", "TABLESAMPLE")
    1475
    1476        expr = f"{bucket}{percent}{rows}{size}"
    1477        if self.TABLESAMPLE_REQUIRES_PARENS:
    1478            expr = f"({expr})"
    1479
    1480        return f"{this} {kind} {method}{expr}{seed}{alias}"

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

    1482    def pivot_sql(self, expression: exp.Pivot) -> str:
    1483        expressions = self.expressions(expression, flat=True)
    1484
    1485        if expression.this:
    1486            this = self.sql(expression, "this")
    1487            if not expressions:
    1488                return f"UNPIVOT {this}"
    1489
    1490            on = f"{self.seg('ON')} {expressions}"
    1491            using = self.expressions(expression, key="using", flat=True)
    1492            using = f"{self.seg('USING')} {using}" if using else ""
    1493            group = self.sql(expression, "group")
    1494            return f"PIVOT {this}{on}{using}{group}"
    1495
    1496        alias = self.sql(expression, "alias")
    1497        alias = f" AS {alias}" if alias else ""
    1498        unpivot = expression.args.get("unpivot")
    1499        direction = "UNPIVOT" if unpivot else "PIVOT"
    1500        field = self.sql(expression, "field")
    1501        include_nulls = expression.args.get("include_nulls")
    1502        if include_nulls is not None:
    1503            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1504        else:
    1505            nulls = ""
    1506        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

def version_sql(self, expression: sqlglot.expressions.Version) -> str: View Source

    1508    def version_sql(self, expression: exp.Version) -> str:
    1509        this = f"FOR {expression.name}"
    1510        kind = expression.text("kind")
    1511        expr = self.sql(expression, "expression")
    1512        return f"{this} {kind} {expr}"

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

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

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

    1517    def update_sql(self, expression: exp.Update) -> str:
    1518        this = self.sql(expression, "this")
    1519        set_sql = self.expressions(expression, flat=True)
    1520        from_sql = self.sql(expression, "from")
    1521        where_sql = self.sql(expression, "where")
    1522        returning = self.sql(expression, "returning")
    1523        order = self.sql(expression, "order")
    1524        limit = self.sql(expression, "limit")
    1525        if self.RETURNING_END:
    1526            expression_sql = f"{from_sql}{where_sql}{returning}"
    1527        else:
    1528            expression_sql = f"{returning}{from_sql}{where_sql}"
    1529        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1530        return self.prepend_ctes(expression, sql)

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

    1532    def values_sql(self, expression: exp.Values) -> str:
    1533        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1534        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
    1535            args = self.expressions(expression)
    1536            alias = self.sql(expression, "alias")
    1537            values = f"VALUES{self.seg('')}{args}"
    1538            values = (
    1539                f"({values})"
    1540                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1541                else values
    1542            )
    1543            return f"{values} AS {alias}" if alias else values
    1544
    1545        # Converts `VALUES...` expression into a series of select unions.
    1546        alias_node = expression.args.get("alias")
    1547        column_names = alias_node and alias_node.columns
    1548
    1549        selects: t.List[exp.Subqueryable] = []
    1550
    1551        for i, tup in enumerate(expression.expressions):
    1552            row = tup.expressions
    1553
    1554            if i == 0 and column_names:
    1555                row = [
    1556                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1557                ]
    1558
    1559            selects.append(exp.Select(expressions=row))
    1560
    1561        if self.pretty:
    1562            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1563            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1564            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1565            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1566            return self.subquery_sql(
    1567                subqueryable.subquery(alias_node and alias_node.this, copy=False)
    1568            )
    1569
    1570        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1571        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1572        return f"({unions}){alias}"

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

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

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

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

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

    1582    def from_sql(self, expression: exp.From) -> str:
    1583        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"

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

    1585    def group_sql(self, expression: exp.Group) -> str:
    1586        group_by = self.op_expressions("GROUP BY", expression)
    1587
    1588        if expression.args.get("all"):
    1589            return f"{group_by} ALL"
    1590
    1591        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1592        grouping_sets = (
    1593            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1594        )
    1595
    1596        cube = expression.args.get("cube", [])
    1597        if seq_get(cube, 0) is True:
    1598            return f"{group_by}{self.seg('WITH CUBE')}"
    1599        else:
    1600            cube_sql = self.expressions(expression, key="cube", indent=False)
    1601            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1602
    1603        rollup = expression.args.get("rollup", [])
    1604        if seq_get(rollup, 0) is True:
    1605            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1606        else:
    1607            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1608            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1609
    1610        groupings = csv(
    1611            grouping_sets,
    1612            cube_sql,
    1613            rollup_sql,
    1614            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1615            sep=self.GROUPINGS_SEP,
    1616        )
    1617
    1618        if expression.args.get("expressions") and groupings:
    1619            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1620
    1621        return f"{group_by}{groupings}"

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

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

def connect_sql(self, expression: sqlglot.expressions.Connect) -> str: View Source

    1627    def connect_sql(self, expression: exp.Connect) -> str:
    1628        start = self.sql(expression, "start")
    1629        start = self.seg(f"START WITH {start}") if start else ""
    1630        connect = self.sql(expression, "connect")
    1631        connect = self.seg(f"CONNECT BY {connect}")
    1632        return start + connect

def prior_sql(self, expression: sqlglot.expressions.Prior) -> str: View Source

    1634    def prior_sql(self, expression: exp.Prior) -> str:
    1635        return f"PRIOR {self.sql(expression, 'this')}"

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

    1637    def join_sql(self, expression: exp.Join) -> str:
    1638        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1639            side = None
    1640        else:
    1641            side = expression.side
    1642
    1643        op_sql = " ".join(
    1644            op
    1645            for op in (
    1646                expression.method,
    1647                "GLOBAL" if expression.args.get("global") else None,
    1648                side,
    1649                expression.kind,
    1650                expression.hint if self.JOIN_HINTS else None,
    1651            )
    1652            if op
    1653        )
    1654        on_sql = self.sql(expression, "on")
    1655        using = expression.args.get("using")
    1656
    1657        if not on_sql and using:
    1658            on_sql = csv(*(self.sql(column) for column in using))
    1659
    1660        this_sql = self.sql(expression, "this")
    1661
    1662        if on_sql:
    1663            on_sql = self.indent(on_sql, skip_first=True)
    1664            space = self.seg(" " * self.pad) if self.pretty else " "
    1665            if using:
    1666                on_sql = f"{space}USING ({on_sql})"
    1667            else:
    1668                on_sql = f"{space}ON {on_sql}"
    1669        elif not op_sql:
    1670            return f", {this_sql}"
    1671
    1672        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1673        return f"{self.seg(op_sql)} {this_sql}{on_sql}"

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

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

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

    1680    def lateral_sql(self, expression: exp.Lateral) -> str:
    1681        this = self.sql(expression, "this")
    1682
    1683        if expression.args.get("view"):
    1684            alias = expression.args["alias"]
    1685            columns = self.expressions(alias, key="columns", flat=True)
    1686            table = f" {alias.name}" if alias.name else ""
    1687            columns = f" AS {columns}" if columns else ""
    1688            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1689            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1690
    1691        alias = self.sql(expression, "alias")
    1692        alias = f" AS {alias}" if alias else ""
    1693        return f"LATERAL {this}{alias}"

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

    1695    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1696        this = self.sql(expression, "this")
    1697
    1698        args = [
    1699            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    1700            for e in (expression.args.get(k) for k in ("offset", "expression"))
    1701            if e
    1702        ]
    1703
    1704        args_sql = ", ".join(self.sql(e) for e in args)
    1705        args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql
    1706        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}"

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

    1708    def offset_sql(self, expression: exp.Offset) -> str:
    1709        this = self.sql(expression, "this")
    1710        expression = expression.expression
    1711        expression = (
    1712            self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression
    1713        )
    1714        return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"

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

    1716    def setitem_sql(self, expression: exp.SetItem) -> str:
    1717        kind = self.sql(expression, "kind")
    1718        kind = f"{kind} " if kind else ""
    1719        this = self.sql(expression, "this")
    1720        expressions = self.expressions(expression)
    1721        collate = self.sql(expression, "collate")
    1722        collate = f" COLLATE {collate}" if collate else ""
    1723        global_ = "GLOBAL " if expression.args.get("global") else ""
    1724        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1726    def set_sql(self, expression: exp.Set) -> str:
    1727        expressions = (
    1728            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1729        )
    1730        tag = " TAG" if expression.args.get("tag") else ""
    1731        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str: View Source

    1733    def pragma_sql(self, expression: exp.Pragma) -> str:
    1734        return f"PRAGMA {self.sql(expression, 'this')}"

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

    1736    def lock_sql(self, expression: exp.Lock) -> str:
    1737        if not self.LOCKING_READS_SUPPORTED:
    1738            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1739            return ""
    1740
    1741        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1742        expressions = self.expressions(expression, flat=True)
    1743        expressions = f" OF {expressions}" if expressions else ""
    1744        wait = expression.args.get("wait")
    1745
    1746        if wait is not None:
    1747            if isinstance(wait, exp.Literal):
    1748                wait = f" WAIT {self.sql(wait)}"
    1749            else:
    1750                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1751
    1752        return f"{lock_type}{expressions}{wait or ''}"

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

    1754    def literal_sql(self, expression: exp.Literal) -> str:
    1755        text = expression.this or ""
    1756        if expression.is_string:
    1757            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    1758        return text

def escape_str(self, text: str) -> str: View Source

    1760    def escape_str(self, text: str) -> str:
    1761        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
    1762        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
    1763            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1764        elif self.pretty:
    1765            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1766        return text

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

    1768    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1769        local = " LOCAL" if expression.args.get("local") else ""
    1770        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1771        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1772        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1773        partition = self.sql(expression, "partition")
    1774        partition = f" {partition}" if partition else ""
    1775        input_format = self.sql(expression, "input_format")
    1776        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1777        serde = self.sql(expression, "serde")
    1778        serde = f" SERDE {serde}" if serde else ""
    1779        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1781    def null_sql(self, *_) -> str:
    1782        return "NULL"

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

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

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

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

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

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

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

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

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

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

    1801    def ordered_sql(self, expression: exp.Ordered) -> str:
    1802        desc = expression.args.get("desc")
    1803        asc = not desc
    1804
    1805        nulls_first = expression.args.get("nulls_first")
    1806        nulls_last = not nulls_first
    1807        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    1808        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    1809        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    1810
    1811        this = self.sql(expression, "this")
    1812
    1813        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    1814        nulls_sort_change = ""
    1815        if nulls_first and (
    1816            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1817        ):
    1818            nulls_sort_change = " NULLS FIRST"
    1819        elif (
    1820            nulls_last
    1821            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1822            and not nulls_are_last
    1823        ):
    1824            nulls_sort_change = " NULLS LAST"
    1825
    1826        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    1827        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1828            null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    1829            this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    1830            nulls_sort_change = ""
    1831
    1832        return f"{this}{sort_order}{nulls_sort_change}"

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

    1834    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1835        partition = self.partition_by_sql(expression)
    1836        order = self.sql(expression, "order")
    1837        measures = self.expressions(expression, key="measures")
    1838        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1839        rows = self.sql(expression, "rows")
    1840        rows = self.seg(rows) if rows else ""
    1841        after = self.sql(expression, "after")
    1842        after = self.seg(after) if after else ""
    1843        pattern = self.sql(expression, "pattern")
    1844        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1845        definition_sqls = [
    1846            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1847            for definition in expression.args.get("define", [])
    1848        ]
    1849        definitions = self.expressions(sqls=definition_sqls)
    1850        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1851        body = "".join(
    1852            (
    1853                partition,
    1854                order,
    1855                measures,
    1856                rows,
    1857                after,
    1858                pattern,
    1859                define,
    1860            )
    1861        )
    1862        alias = self.sql(expression, "alias")
    1863        alias = f" {alias}" if alias else ""
    1864        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    1866    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1867        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
    1868
    1869        # If the limit is generated as TOP, we need to ensure it's not generated twice
    1870        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
    1871
    1872        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1873            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    1874        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1875            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    1876
    1877        fetch = isinstance(limit, exp.Fetch)
    1878
    1879        offset_limit_modifiers = (
    1880            self.offset_limit_modifiers(expression, fetch, limit)
    1881            if with_offset_limit_modifiers
    1882            else []
    1883        )
    1884
    1885        return csv(
    1886            *sqls,
    1887            *[self.sql(join) for join in expression.args.get("joins") or []],
    1888            self.sql(expression, "connect"),
    1889            self.sql(expression, "match"),
    1890            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1891            self.sql(expression, "where"),
    1892            self.sql(expression, "group"),
    1893            self.sql(expression, "having"),
    1894            *self.after_having_modifiers(expression),
    1895            self.sql(expression, "order"),
    1896            *offset_limit_modifiers,
    1897            *self.after_limit_modifiers(expression),
    1898            sep="",
    1899        )

def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]: View Source

    1901    def offset_limit_modifiers(
    1902        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    1903    ) -> t.List[str]:
    1904        return [
    1905            self.sql(expression, "offset") if fetch else self.sql(limit),
    1906            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1907        ]

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

    1909    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1910        return [
    1911            self.sql(expression, "qualify"),
    1912            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1913            if expression.args.get("windows")
    1914            else "",
    1915            self.sql(expression, "distribute"),
    1916            self.sql(expression, "sort"),
    1917            self.sql(expression, "cluster"),
    1918        ]

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

    1920    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1921        locks = self.expressions(expression, key="locks", sep=" ")
    1922        locks = f" {locks}" if locks else ""
    1923        return [locks, self.sql(expression, "sample")]

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

    1925    def select_sql(self, expression: exp.Select) -> str:
    1926        hint = self.sql(expression, "hint")
    1927        distinct = self.sql(expression, "distinct")
    1928        distinct = f" {distinct}" if distinct else ""
    1929        kind = self.sql(expression, "kind").upper()
    1930        limit = expression.args.get("limit")
    1931        top = (
    1932            self.limit_sql(limit, top=True)
    1933            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
    1934            else ""
    1935        )
    1936
    1937        expressions = self.expressions(expression)
    1938
    1939        if kind:
    1940            if kind in self.SELECT_KINDS:
    1941                kind = f" AS {kind}"
    1942            else:
    1943                if kind == "STRUCT":
    1944                    expressions = self.expressions(
    1945                        sqls=[
    1946                            self.sql(
    1947                                exp.Struct(
    1948                                    expressions=[
    1949                                        exp.column(e.output_name).eq(
    1950                                            e.this if isinstance(e, exp.Alias) else e
    1951                                        )
    1952                                        for e in expression.expressions
    1953                                    ]
    1954                                )
    1955                            )
    1956                        ]
    1957                    )
    1958                kind = ""
    1959
    1960        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    1961        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    1962        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    1963        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1964        sql = self.query_modifiers(
    1965            expression,
    1966            f"SELECT{top_distinct}{kind}{expressions}",
    1967            self.sql(expression, "into", comment=False),
    1968            self.sql(expression, "from", comment=False),
    1969        )
    1970        return self.prepend_ctes(expression, sql)

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

    1972    def schema_sql(self, expression: exp.Schema) -> str:
    1973        this = self.sql(expression, "this")
    1974        sql = self.schema_columns_sql(expression)
    1975        return f"{this} {sql}" if this and sql else this or sql

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

    1977    def schema_columns_sql(self, expression: exp.Schema) -> str:
    1978        if expression.expressions:
    1979            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1980        return ""

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

    1982    def star_sql(self, expression: exp.Star) -> str:
    1983        except_ = self.expressions(expression, key="except", flat=True)
    1984        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1985        replace = self.expressions(expression, key="replace", flat=True)
    1986        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1987        return f"*{except_}{replace}"

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

    1989    def parameter_sql(self, expression: exp.Parameter) -> str:
    1990        this = self.sql(expression, "this")
    1991        return f"{self.PARAMETER_TOKEN}{this}"

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

    1993    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1994        this = self.sql(expression, "this")
    1995        kind = expression.text("kind")
    1996        if kind:
    1997            kind = f"{kind}."
    1998        return f"@@{kind}{this}"

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

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

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

    2003    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2004        alias = self.sql(expression, "alias")
    2005        alias = f"{sep}{alias}" if alias else ""
    2006
    2007        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2008        pivots = f" {pivots}" if pivots else ""
    2009
    2010        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2011        return self.prepend_ctes(expression, sql)

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

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

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

    2017    def union_sql(self, expression: exp.Union) -> str:
    2018        return self.prepend_ctes(
    2019            expression,
    2020            self.set_operation(expression, self.union_op(expression)),
    2021        )

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

    2023    def union_op(self, expression: exp.Union) -> str:
    2024        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2025        kind = kind if expression.args.get("distinct") else " ALL"
    2026        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2027        return f"UNION{kind}{by_name}"

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

    2029    def unnest_sql(self, expression: exp.Unnest) -> str:
    2030        args = self.expressions(expression, flat=True)
    2031
    2032        alias = expression.args.get("alias")
    2033        offset = expression.args.get("offset")
    2034
    2035        if self.UNNEST_WITH_ORDINALITY:
    2036            if alias and isinstance(offset, exp.Expression):
    2037                alias.append("columns", offset)
    2038
    2039        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2040            columns = alias.columns
    2041            alias = self.sql(columns[0]) if columns else ""
    2042        else:
    2043            alias = self.sql(alias)
    2044
    2045        alias = f" AS {alias}" if alias else alias
    2046        if self.UNNEST_WITH_ORDINALITY:
    2047            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2048        else:
    2049            if isinstance(offset, exp.Expression):
    2050                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2051            elif offset:
    2052                suffix = f"{alias} WITH OFFSET"
    2053            else:
    2054                suffix = alias
    2055
    2056        return f"UNNEST({args}){suffix}"

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

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

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

    2062    def window_sql(self, expression: exp.Window) -> str:
    2063        this = self.sql(expression, "this")
    2064        partition = self.partition_by_sql(expression)
    2065        order = expression.args.get("order")
    2066        order = self.order_sql(order, flat=True) if order else ""
    2067        spec = self.sql(expression, "spec")
    2068        alias = self.sql(expression, "alias")
    2069        over = self.sql(expression, "over") or "OVER"
    2070
    2071        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2072
    2073        first = expression.args.get("first")
    2074        if first is None:
    2075            first = ""
    2076        else:
    2077            first = "FIRST" if first else "LAST"
    2078
    2079        if not partition and not order and not spec and alias:
    2080            return f"{this} {alias}"
    2081
    2082        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2083        return f"{this} ({args})"

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

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

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

    2089    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2090        kind = self.sql(expression, "kind")
    2091        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2092        end = (
    2093            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2094            or "CURRENT ROW"
    2095        )
    2096        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2103    def between_sql(self, expression: exp.Between) -> str:
    2104        this = self.sql(expression, "this")
    2105        low = self.sql(expression, "low")
    2106        high = self.sql(expression, "high")
    2107        return f"{this} BETWEEN {low} AND {high}"

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

    2109    def bracket_sql(self, expression: exp.Bracket) -> str:
    2110        expressions = apply_index_offset(
    2111            expression.this,
    2112            expression.expressions,
    2113            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2114        )
    2115        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2116        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    2121    def any_sql(self, expression: exp.Any) -> str:
    2122        this = self.sql(expression, "this")
    2123        if isinstance(expression.this, exp.Subqueryable):
    2124            this = self.wrap(this)
    2125        return f"ANY {this}"

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

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

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

    2130    def case_sql(self, expression: exp.Case) -> str:
    2131        this = self.sql(expression, "this")
    2132        statements = [f"CASE {this}" if this else "CASE"]
    2133
    2134        for e in expression.args["ifs"]:
    2135            statements.append(f"WHEN {self.sql(e, 'this')}")
    2136            statements.append(f"THEN {self.sql(e, 'true')}")
    2137
    2138        default = self.sql(expression, "default")
    2139
    2140        if default:
    2141            statements.append(f"ELSE {default}")
    2142
    2143        statements.append("END")
    2144
    2145        if self.pretty and self.text_width(statements) > self.max_text_width:
    2146            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2147
    2148        return " ".join(statements)

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

    2150    def constraint_sql(self, expression: exp.Constraint) -> str:
    2151        this = self.sql(expression, "this")
    2152        expressions = self.expressions(expression, flat=True)
    2153        return f"CONSTRAINT {this} {expressions}"

def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str: View Source

    2155    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2156        order = expression.args.get("order")
    2157        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2158        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"

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

    2160    def extract_sql(self, expression: exp.Extract) -> str:
    2161        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2162        expression_sql = self.sql(expression, "expression")
    2163        return f"EXTRACT({this} FROM {expression_sql})"

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

    2165    def trim_sql(self, expression: exp.Trim) -> str:
    2166        trim_type = self.sql(expression, "position")
    2167
    2168        if trim_type == "LEADING":
    2169            return self.func("LTRIM", expression.this)
    2170        elif trim_type == "TRAILING":
    2171            return self.func("RTRIM", expression.this)
    2172        else:
    2173            return self.func("TRIM", expression.this, expression.expression)

def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]: View Source

    2175    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2176        args = expression.expressions
    2177        if isinstance(expression, exp.ConcatWs):
    2178            args = args[1:]  # Skip the delimiter
    2179
    2180        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2181            args = [exp.cast(e, "text") for e in args]
    2182
    2183        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2184            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2185
    2186        return args

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

    2188    def concat_sql(self, expression: exp.Concat) -> str:
    2189        expressions = self.convert_concat_args(expression)
    2190
    2191        # Some dialects don't allow a single-argument CONCAT call
    2192        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2193            return self.sql(expressions[0])
    2194
    2195        return self.func("CONCAT", *expressions)

def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str: View Source

    2197    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2198        return self.func(
    2199            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2200        )

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

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

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

    2206    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2207        expressions = self.expressions(expression, flat=True)
    2208        reference = self.sql(expression, "reference")
    2209        reference = f" {reference}" if reference else ""
    2210        delete = self.sql(expression, "delete")
    2211        delete = f" ON DELETE {delete}" if delete else ""
    2212        update = self.sql(expression, "update")
    2213        update = f" ON UPDATE {update}" if update else ""
    2214        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2216    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2217        expressions = self.expressions(expression, flat=True)
    2218        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2219        options = f" {options}" if options else ""
    2220        return f"PRIMARY KEY ({expressions}){options}"

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

    2222    def if_sql(self, expression: exp.If) -> str:
    2223        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))

def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str: View Source

    2225    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2226        modifier = expression.args.get("modifier")
    2227        modifier = f" {modifier}" if modifier else ""
    2228        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"

def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str: View Source

    2230    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2231        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"

def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str: View Source

    2233    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2234        return f"{self.sql(expression, 'this')} FORMAT JSON"

def jsonobject_sql(self, expression: sqlglot.expressions.JSONObject) -> str: View Source

    2236    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    2237        null_handling = expression.args.get("null_handling")
    2238        null_handling = f" {null_handling}" if null_handling else ""
    2239        unique_keys = expression.args.get("unique_keys")
    2240        if unique_keys is not None:
    2241            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2242        else:
    2243            unique_keys = ""
    2244        return_type = self.sql(expression, "return_type")
    2245        return_type = f" RETURNING {return_type}" if return_type else ""
    2246        encoding = self.sql(expression, "encoding")
    2247        encoding = f" ENCODING {encoding}" if encoding else ""
    2248        return self.func(
    2249            "JSON_OBJECT",
    2250            *expression.expressions,
    2251            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2252        )

def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str: View Source

    2254    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2255        null_handling = expression.args.get("null_handling")
    2256        null_handling = f" {null_handling}" if null_handling else ""
    2257        return_type = self.sql(expression, "return_type")
    2258        return_type = f" RETURNING {return_type}" if return_type else ""
    2259        strict = " STRICT" if expression.args.get("strict") else ""
    2260        return self.func(
    2261            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2262        )

def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str: View Source

    2264    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2265        this = self.sql(expression, "this")
    2266        order = self.sql(expression, "order")
    2267        null_handling = expression.args.get("null_handling")
    2268        null_handling = f" {null_handling}" if null_handling else ""
    2269        return_type = self.sql(expression, "return_type")
    2270        return_type = f" RETURNING {return_type}" if return_type else ""
    2271        strict = " STRICT" if expression.args.get("strict") else ""
    2272        return self.func(
    2273            "JSON_ARRAYAGG",
    2274            this,
    2275            suffix=f"{order}{null_handling}{return_type}{strict})",
    2276        )

def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str: View Source

    2278    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2279        path = self.sql(expression, "path")
    2280        path = f" PATH {path}" if path else ""
    2281        nested_schema = self.sql(expression, "nested_schema")
    2282
    2283        if nested_schema:
    2284            return f"NESTED{path} {nested_schema}"
    2285
    2286        this = self.sql(expression, "this")
    2287        kind = self.sql(expression, "kind")
    2288        kind = f" {kind}" if kind else ""
    2289        return f"{this}{kind}{path}"

def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str: View Source

    2291    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    2292        return self.func("COLUMNS", *expression.expressions)

def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str: View Source

    2294    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2295        this = self.sql(expression, "this")
    2296        path = self.sql(expression, "path")
    2297        path = f", {path}" if path else ""
    2298        error_handling = expression.args.get("error_handling")
    2299        error_handling = f" {error_handling}" if error_handling else ""
    2300        empty_handling = expression.args.get("empty_handling")
    2301        empty_handling = f" {empty_handling}" if empty_handling else ""
    2302        schema = self.sql(expression, "schema")
    2303        return self.func(
    2304            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2305        )

def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str: View Source

    2307    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2308        this = self.sql(expression, "this")
    2309        kind = self.sql(expression, "kind")
    2310        path = self.sql(expression, "path")
    2311        path = f" {path}" if path else ""
    2312        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2313        return f"{this} {kind}{path}{as_json}"

def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str: View Source

    2315    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2316        this = self.sql(expression, "this")
    2317        path = self.sql(expression, "path")
    2318        path = f", {path}" if path else ""
    2319        expressions = self.expressions(expression)
    2320        with_ = (
    2321            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2322            if expressions
    2323            else ""
    2324        )
    2325        return f"OPENJSON({this}{path}){with_}"

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

    2327    def in_sql(self, expression: exp.In) -> str:
    2328        query = expression.args.get("query")
    2329        unnest = expression.args.get("unnest")
    2330        field = expression.args.get("field")
    2331        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2332
    2333        if query:
    2334            in_sql = self.wrap(query)
    2335        elif unnest:
    2336            in_sql = self.in_unnest_op(unnest)
    2337        elif field:
    2338            in_sql = self.sql(field)
    2339        else:
    2340            in_sql = f"({self.expressions(expression, flat=True)})"
    2341
    2342        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2347    def interval_sql(self, expression: exp.Interval) -> str:
    2348        unit = self.sql(expression, "unit")
    2349        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2350            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    2351        unit = f" {unit}" if unit else ""
    2352
    2353        if self.SINGLE_STRING_INTERVAL:
    2354            this = expression.this.name if expression.this else ""
    2355            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2356
    2357        this = self.sql(expression, "this")
    2358        if this:
    2359            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2360            this = f" {this}" if unwrapped else f" ({this})"
    2361
    2362        return f"INTERVAL{this}{unit}"

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

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

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

    2367    def reference_sql(self, expression: exp.Reference) -> str:
    2368        this = self.sql(expression, "this")
    2369        expressions = self.expressions(expression, flat=True)
    2370        expressions = f"({expressions})" if expressions else ""
    2371        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2372        options = f" {options}" if options else ""
    2373        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    2378    def paren_sql(self, expression: exp.Paren) -> str:
    2379        if isinstance(expression.unnest(), exp.Select):
    2380            sql = self.wrap(expression)
    2381        else:
    2382            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2383            sql = f"({sql}{self.seg(')', sep='')}"
    2384
    2385        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    2396    def alias_sql(self, expression: exp.Alias) -> str:
    2397        alias = self.sql(expression, "alias")
    2398        alias = f" AS {alias}" if alias else ""
    2399        return f"{self.sql(expression, 'this')}{alias}"

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

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

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

    2404    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2405        this = self.sql(expression, "this")
    2406        zone = self.sql(expression, "zone")
    2407        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

def xor_sql(self, expression: sqlglot.expressions.Xor) -> str: View Source

    2415    def xor_sql(self, expression: exp.Xor) -> str:
    2416        return self.connector_sql(expression, "XOR")

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

    2418    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2419        if not self.pretty:
    2420            return self.binary(expression, op)
    2421
    2422        sqls = tuple(
    2423            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2424            for i, e in enumerate(expression.flatten(unnest=False))
    2425        )
    2426
    2427        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2428        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str: View Source

    2448    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2449        format_sql = self.sql(expression, "format")
    2450        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2451        to_sql = self.sql(expression, "to")
    2452        to_sql = f" {to_sql}" if to_sql else ""
    2453        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"

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

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

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

    2459    def collate_sql(self, expression: exp.Collate) -> str:
    2460        if self.COLLATE_IS_FUNC:
    2461            return self.function_fallback_sql(expression)
    2462        return self.binary(expression, "COLLATE")

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

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

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

    2467    def comment_sql(self, expression: exp.Comment) -> str:
    2468        this = self.sql(expression, "this")
    2469        kind = expression.args["kind"]
    2470        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2471        expression_sql = self.sql(expression, "expression")
    2472        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str: View Source

    2474    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2475        this = self.sql(expression, "this")
    2476        delete = " DELETE" if expression.args.get("delete") else ""
    2477        recompress = self.sql(expression, "recompress")
    2478        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2479        to_disk = self.sql(expression, "to_disk")
    2480        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2481        to_volume = self.sql(expression, "to_volume")
    2482        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2483        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str: View Source

    2485    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2486        where = self.sql(expression, "where")
    2487        group = self.sql(expression, "group")
    2488        aggregates = self.expressions(expression, key="aggregates")
    2489        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2490
    2491        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2492            return f"TTL {self.expressions(expression, flat=True)}"
    2493
    2494        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2499    def commit_sql(self, expression: exp.Commit) -> str:
    2500        chain = expression.args.get("chain")
    2501        if chain is not None:
    2502            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2503
    2504        return f"COMMIT{chain or ''}"

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

    2506    def rollback_sql(self, expression: exp.Rollback) -> str:
    2507        savepoint = expression.args.get("savepoint")
    2508        savepoint = f" TO {savepoint}" if savepoint else ""
    2509        return f"ROLLBACK{savepoint}"

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

    2511    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2512        this = self.sql(expression, "this")
    2513
    2514        dtype = self.sql(expression, "dtype")
    2515        if dtype:
    2516            collate = self.sql(expression, "collate")
    2517            collate = f" COLLATE {collate}" if collate else ""
    2518            using = self.sql(expression, "using")
    2519            using = f" USING {using}" if using else ""
    2520            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2521
    2522        default = self.sql(expression, "default")
    2523        if default:
    2524            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2525
    2526        if not expression.args.get("drop"):
    2527            self.unsupported("Unsupported ALTER COLUMN syntax")
    2528
    2529        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2531    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2532        if not self.RENAME_TABLE_WITH_DB:
    2533            # Remove db from tables
    2534            expression = expression.transform(
    2535                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2536            )
    2537        this = self.sql(expression, "this")
    2538        return f"RENAME TO {this}"

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

    2540    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2541        actions = expression.args["actions"]
    2542
    2543        if isinstance(actions[0], exp.ColumnDef):
    2544            actions = self.add_column_sql(expression)
    2545        elif isinstance(actions[0], exp.Schema):
    2546            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2547        elif isinstance(actions[0], exp.Delete):
    2548            actions = self.expressions(expression, key="actions", flat=True)
    2549        else:
    2550            actions = self.expressions(expression, key="actions", flat=True)
    2551
    2552        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2553        only = " ONLY" if expression.args.get("only") else ""
    2554        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"

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

    2556    def add_column_sql(self, expression: exp.AlterTable) -> str:
    2557        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    2558            return self.expressions(
    2559                expression,
    2560                key="actions",
    2561                prefix="ADD COLUMN ",
    2562            )
    2563        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    2565    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2566        expressions = self.expressions(expression)
    2567        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2568        return f"DROP{exists}{expressions}"

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

    2570    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2571        this = self.sql(expression, "this")
    2572        expression_ = self.sql(expression, "expression")
    2573        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2574
    2575        enforced = expression.args.get("enforced")
    2576        if enforced is not None:
    2577            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2578
    2579        return f"{add_constraint} {expression_}"

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

    2581    def distinct_sql(self, expression: exp.Distinct) -> str:
    2582        this = self.expressions(expression, flat=True)
    2583        this = f" {this}" if this else ""
    2584
    2585        on = self.sql(expression, "on")
    2586        on = f" ON {on}" if on else ""
    2587        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2595    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2596        return self.sql(
    2597            exp.Cast(
    2598                this=exp.Div(this=expression.this, expression=expression.expression),
    2599                to=exp.DataType(this=exp.DataType.Type.INT),
    2600            )
    2601        )

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

    2603    def dpipe_sql(self, expression: exp.DPipe) -> str:
    2604        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2605            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
    2606        return self.binary(expression, "||")

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

    2608    def div_sql(self, expression: exp.Div) -> str:
    2609        l, r = expression.left, expression.right
    2610
    2611        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    2612            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    2613
    2614        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    2615            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
    2616                *exp.DataType.FLOAT_TYPES
    2617            ):
    2618                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    2619
    2620        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    2621            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    2622                return self.sql(
    2623                    exp.cast(
    2624                        l / r,
    2625                        to=exp.DataType.Type.BIGINT,
    2626                    )
    2627                )
    2628
    2629        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str: View Source

    2643    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    2644        return self.binary(expression, ":=")

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

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

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

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

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

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

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

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

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

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

def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str: View Source

    2661    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    2662        return self.binary(expression, "ILIKE ANY")

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

    2664    def is_sql(self, expression: exp.Is) -> str:
    2665        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    2666            return self.sql(
    2667                expression.this if expression.expression.this else exp.not_(expression.this)
    2668            )
    2669        return self.binary(expression, "IS")

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

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

def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str: View Source

    2674    def likeany_sql(self, expression: exp.LikeAny) -> str:
    2675        return self.binary(expression, "LIKE ANY")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2710    def trycast_sql(self, expression: exp.TryCast) -> str:
    2711        return self.cast_sql(expression, safe_prefix="TRY_")

def log_sql(self, expression: sqlglot.expressions.Log) -> str: View Source

    2713    def log_sql(self, expression: exp.Log) -> str:
    2714        this = expression.this
    2715        expr = expression.expression
    2716
    2717        if not self.dialect.LOG_BASE_FIRST:
    2718            this, expr = expr, this
    2719
    2720        return self.func("LOG", this, expr)

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

    2722    def use_sql(self, expression: exp.Use) -> str:
    2723        kind = self.sql(expression, "kind")
    2724        kind = f" {kind}" if kind else ""
    2725        this = self.sql(expression, "this")
    2726        this = f" {this}" if this else ""
    2727        return f"USE{kind}{this}"

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

    2729    def binary(self, expression: exp.Binary, op: str) -> str:
    2730        op = self.maybe_comment(op, comments=expression.comments)
    2731        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"

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

    2733    def function_fallback_sql(self, expression: exp.Func) -> str:
    2734        args = []
    2735
    2736        for key in expression.arg_types:
    2737            arg_value = expression.args.get(key)
    2738
    2739            if isinstance(arg_value, list):
    2740                for value in arg_value:
    2741                    args.append(value)
    2742            elif arg_value is not None:
    2743                args.append(arg_value)
    2744
    2745        if self.normalize_functions:
    2746            name = expression.sql_name()
    2747        else:
    2748            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    2749
    2750        return self.func(name, *args)

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

    2752    def func(
    2753        self,
    2754        name: str,
    2755        *args: t.Optional[exp.Expression | str],
    2756        prefix: str = "(",
    2757        suffix: str = ")",
    2758    ) -> str:
    2759        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"

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

    2761    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    2762        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    2763        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
    2764            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    2765        return ", ".join(arg_sqls)

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

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

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

    2770    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2771        return format_time(
    2772            self.sql(expression, "format"),
    2773            self.dialect.INVERSE_TIME_MAPPING,
    2774            self.dialect.INVERSE_TIME_TRIE,
    2775        )

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

    2777    def expressions(
    2778        self,
    2779        expression: t.Optional[exp.Expression] = None,
    2780        key: t.Optional[str] = None,
    2781        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    2782        flat: bool = False,
    2783        indent: bool = True,
    2784        skip_first: bool = False,
    2785        sep: str = ", ",
    2786        prefix: str = "",
    2787    ) -> str:
    2788        expressions = expression.args.get(key or "expressions") if expression else sqls
    2789
    2790        if not expressions:
    2791            return ""
    2792
    2793        if flat:
    2794            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    2795
    2796        num_sqls = len(expressions)
    2797
    2798        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2799        pad = " " * self.pad
    2800        stripped_sep = sep.strip()
    2801
    2802        result_sqls = []
    2803        for i, e in enumerate(expressions):
    2804            sql = self.sql(e, comment=False)
    2805            if not sql:
    2806                continue
    2807
    2808            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2809
    2810            if self.pretty:
    2811                if self.leading_comma:
    2812                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2813                else:
    2814                    result_sqls.append(
    2815                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2816                    )
    2817            else:
    2818                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2819
    2820        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2821        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql

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

    2823    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2824        flat = flat or isinstance(expression.parent, exp.Properties)
    2825        expressions_sql = self.expressions(expression, flat=flat)
    2826        if flat:
    2827            return f"{op} {expressions_sql}"
    2828        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    2830    def naked_property(self, expression: exp.Property) -> str:
    2831        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2832        if not property_name:
    2833            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2834        return f"{property_name} {self.sql(expression, 'this')}"

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

    2836    def set_operation(self, expression: exp.Union, op: str) -> str:
    2837        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
    2838        op = self.seg(op)
    2839        return self.query_modifiers(
    2840            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2841        )

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

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

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

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

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

    2849    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2850        this = self.sql(expression, "this")
    2851        expressions = self.no_identify(self.expressions, expression)
    2852        expressions = (
    2853            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2854        )
    2855        return f"{this}{expressions}"

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

    2857    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2858        this = self.sql(expression, "this")
    2859        expressions = self.expressions(expression, flat=True)
    2860        return f"{this}({expressions})"

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

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

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

    2865    def when_sql(self, expression: exp.When) -> str:
    2866        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2867        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2868        condition = self.sql(expression, "condition")
    2869        condition = f" AND {condition}" if condition else ""
    2870
    2871        then_expression = expression.args.get("then")
    2872        if isinstance(then_expression, exp.Insert):
    2873            then = f"INSERT {self.sql(then_expression, 'this')}"
    2874            if "expression" in then_expression.args:
    2875                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2876        elif isinstance(then_expression, exp.Update):
    2877            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2878                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2879            else:
    2880                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2881        else:
    2882            then = self.sql(then_expression)
    2883        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    2885    def merge_sql(self, expression: exp.Merge) -> str:
    2886        table = expression.this
    2887        table_alias = ""
    2888
    2889        hints = table.args.get("hints")
    2890        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    2891            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    2892            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    2893
    2894        this = self.sql(table)
    2895        using = f"USING {self.sql(expression, 'using')}"
    2896        on = f"ON {self.sql(expression, 'on')}"
    2897        expressions = self.expressions(expression, sep=" ")
    2898
    2899        return self.prepend_ctes(
    2900            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
    2901        )

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

    2903    def tochar_sql(self, expression: exp.ToChar) -> str:
    2904        if expression.args.get("format"):
    2905            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2906
    2907        return self.sql(exp.cast(expression.this, "text"))

def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str: View Source

    2909    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2910        this = self.sql(expression, "this")
    2911        kind = self.sql(expression, "kind")
    2912        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2913        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2914        return f"{this}({kind}{args})"

def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str: View Source

    2916    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2917        this = self.sql(expression, "this")
    2918        max = self.sql(expression, "max")
    2919        min = self.sql(expression, "min")
    2920        return f"{this}(MIN {min} MAX {max})"

def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str: View Source

    2922    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    2923        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"

def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str: View Source

    2925    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    2926        return ""

def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str: View Source

    2928    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    2929        expressions = self.expressions(expression, key="expressions", flat=True)
    2930        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    2931        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    2932        buckets = self.sql(expression, "buckets")
    2933        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str: View Source

    2935    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    2936        this = self.sql(expression, "this")
    2937        having = self.sql(expression, "having")
    2938
    2939        if having:
    2940            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    2941
    2942        return self.func("ANY_VALUE", this)

def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str: View Source

    2944    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    2945        transform = self.func("TRANSFORM", *expression.expressions)
    2946        row_format_before = self.sql(expression, "row_format_before")
    2947        row_format_before = f" {row_format_before}" if row_format_before else ""
    2948        record_writer = self.sql(expression, "record_writer")
    2949        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    2950        using = f" USING {self.sql(expression, 'command_script')}"
    2951        schema = self.sql(expression, "schema")
    2952        schema = f" AS {schema}" if schema else ""
    2953        row_format_after = self.sql(expression, "row_format_after")
    2954        row_format_after = f" {row_format_after}" if row_format_after else ""
    2955        record_reader = self.sql(expression, "record_reader")
    2956        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    2957        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"

def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str: View Source

    2959    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    2960        key_block_size = self.sql(expression, "key_block_size")
    2961        if key_block_size:
    2962            return f"KEY_BLOCK_SIZE = {key_block_size}"
    2963
    2964        using = self.sql(expression, "using")
    2965        if using:
    2966            return f"USING {using}"
    2967
    2968        parser = self.sql(expression, "parser")
    2969        if parser:
    2970            return f"WITH PARSER {parser}"
    2971
    2972        comment = self.sql(expression, "comment")
    2973        if comment:
    2974            return f"COMMENT {comment}"
    2975
    2976        visible = expression.args.get("visible")
    2977        if visible is not None:
    2978            return "VISIBLE" if visible else "INVISIBLE"
    2979
    2980        engine_attr = self.sql(expression, "engine_attr")
    2981        if engine_attr:
    2982            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    2983
    2984        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    2985        if secondary_engine_attr:
    2986            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    2987
    2988        self.unsupported("Unsupported index constraint option.")
    2989        return ""

def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str: View Source

    2991    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    2992        kind = self.sql(expression, "kind")
    2993        kind = f"{kind} INDEX" if kind else "INDEX"
    2994        this = self.sql(expression, "this")
    2995        this = f" {this}" if this else ""
    2996        index_type = self.sql(expression, "index_type")
    2997        index_type = f" USING {index_type}" if index_type else ""
    2998        schema = self.sql(expression, "schema")
    2999        schema = f" {schema}" if schema else ""
    3000        options = self.expressions(expression, key="options", sep=" ")
    3001        options = f" {options}" if options else ""
    3002        return f"{kind}{this}{index_type}{schema}{options}"

def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str: View Source

    3004    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3005        if self.NVL2_SUPPORTED:
    3006            return self.function_fallback_sql(expression)
    3007
    3008        case = exp.Case().when(
    3009            expression.this.is_(exp.null()).not_(copy=False),
    3010            expression.args["true"],
    3011            copy=False,
    3012        )
    3013        else_cond = expression.args.get("false")
    3014        if else_cond:
    3015            case.else_(else_cond, copy=False)
    3016
    3017        return self.sql(case)

def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str: View Source

    3019    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3020        this = self.sql(expression, "this")
    3021        expr = self.sql(expression, "expression")
    3022        iterator = self.sql(expression, "iterator")
    3023        condition = self.sql(expression, "condition")
    3024        condition = f" IF {condition}" if condition else ""
    3025        return f"{this} FOR {expr} IN {iterator}{condition}"

def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str: View Source

    3027    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    3028        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"

def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str: View Source

    3030    def opclass_sql(self, expression: exp.Opclass) -> str:
    3031        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"

def predict_sql(self, expression: sqlglot.expressions.Predict) -> str: View Source

    3033    def predict_sql(self, expression: exp.Predict) -> str:
    3034        model = self.sql(expression, "this")
    3035        model = f"MODEL {model}"
    3036        table = self.sql(expression, "expression")
    3037        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3038        parameters = self.sql(expression, "params_struct")
    3039        return self.func("PREDICT", model, table, parameters or None)

def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str: View Source

    3041    def forin_sql(self, expression: exp.ForIn) -> str:
    3042        this = self.sql(expression, "this")
    3043        expression_sql = self.sql(expression, "expression")
    3044        return f"FOR {this} DO {expression_sql}"

def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str: View Source

    3046    def refresh_sql(self, expression: exp.Refresh) -> str:
    3047        this = self.sql(expression, "this")
    3048        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3049        return f"REFRESH {table}{this}"

def operator_sql(self, expression: sqlglot.expressions.Operator) -> str: View Source

    3051    def operator_sql(self, expression: exp.Operator) -> str:
    3052        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")