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

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

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

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) View Source

    394    def __init__(
    395        self,
    396        pretty: t.Optional[bool] = None,
    397        identify: str | bool = False,
    398        normalize: bool = False,
    399        pad: int = 2,
    400        indent: int = 2,
    401        normalize_functions: t.Optional[str | bool] = None,
    402        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    403        max_unsupported: int = 3,
    404        leading_comma: bool = False,
    405        max_text_width: int = 80,
    406        comments: bool = True,
    407    ):
    408        import sqlglot
    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
    421        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    422        self.normalize_functions = (
    423            self.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    424        )
    425
    426        self.unsupported_messages: t.List[str] = []
    427        self._escaped_quote_end: str = self.TOKENIZER_CLASS.STRING_ESCAPES[0] + self.QUOTE_END
    428        self._escaped_identifier_end: str = (
    429            self.TOKENIZER_CLASS.IDENTIFIER_ESCAPES[0] + self.IDENTIFIER_END
    430        )
    431        self._cache: t.Optional[t.Dict[int, str]] = None

TRANSFORMS = {<class 'sqlglot.expressions.DateAdd'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TsOrDsAdd'>: <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.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.PathColumnConstraint'>: <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.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>>}
LOG_BASE_FIRST = True
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'
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_ADD_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
SUPPORTS_PARAMETERS = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
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.tokens.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.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.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <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.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'>}
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'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
INVERSE_TIME_MAPPING: Dict[str, str] = {}
INVERSE_TIME_TRIE: Dict = {}
INVERSE_ESCAPE_SEQUENCES: Dict[str, str] = {}
INDEX_OFFSET = 0
UNNEST_COLUMN_ONLY = False
ALIAS_POST_TABLESAMPLE = False
IDENTIFIERS_CAN_START_WITH_DIGIT = False
STRICT_STRING_CONCAT = False
NORMALIZE_FUNCTIONS: bool | str = 'upper'
NULL_ORDERING = 'nulls_are_small'
@classmethod
def can_identify(text: str, identify: str | bool = 'safe') -> bool: View Source

    279    @classmethod
    280    def can_identify(cls, text: str, identify: str | bool = "safe") -> bool:
    281        """Checks if text can be identified given an identify option.
    282
    283        Args:
    284            text: The text to check.
    285            identify:
    286                "always" or `True`: Always returns true.
    287                "safe": True if the identifier is case-insensitive.
    288
    289        Returns:
    290            Whether or not the given text can be identified.
    291        """
    292        if identify is True or identify == "always":
    293            return True
    294
    295        if identify == "safe":
    296            return not cls.case_sensitive(text)
    297
    298        return False

Checks if text can be identified given an identify option.

###### Arguments:

- text: The text to check.
- identify: "always" or `True`: Always returns true. "safe": True if the identifier is case-insensitive.

###### Returns:

> Whether or not the given text can be identified.

QUOTE_START = "'"
QUOTE_END = "'"
IDENTIFIER_START = '"'
IDENTIFIER_END = '"'
TOKENIZER_CLASS = <class 'sqlglot.tokens.Tokenizer'>
BIT_START: Optional[str] = None
BIT_END: Optional[str] = None
HEX_START: Optional[str] = None
HEX_END: Optional[str] = None
BYTE_START: Optional[str] = None
BYTE_END: Optional[str] = None
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: Optional[sqlglot.expressions.Expression], cache: Optional[Dict[int, str]] = None) -> str: View Source

    433    def generate(
    434        self,
    435        expression: t.Optional[exp.Expression],
    436        cache: t.Optional[t.Dict[int, str]] = None,
    437    ) -> str:
    438        """
    439        Generates the SQL string corresponding to the given syntax tree.
    440
    441        Args:
    442            expression: The syntax tree.
    443            cache: An optional sql string cache. This leverages the hash of an Expression
    444                which can be slow to compute, so only use it if you set _hash on each node.
    445
    446        Returns:
    447            The SQL string corresponding to `expression`.
    448        """
    449        if cache is not None:
    450            self._cache = cache
    451
    452        self.unsupported_messages = []
    453        sql = self.sql(expression).strip()
    454        self._cache = None
    455
    456        if self.unsupported_level == ErrorLevel.IGNORE:
    457            return sql
    458
    459        if self.unsupported_level == ErrorLevel.WARN:
    460            for msg in self.unsupported_messages:
    461                logger.warning(msg)
    462        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    463            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    464
    465        if self.pretty:
    466            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    467        return sql

Generates the SQL string corresponding to the given syntax tree.

###### Arguments:

- expression: The syntax tree.
- cache: An optional sql string cache. This leverages the hash of an Expression which can be slow to compute, so only use it if you set _hash on each node.

###### Returns:

> The SQL string corresponding to `expression`.

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

    469    def unsupported(self, message: str) -> None:
    470        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    471            raise UnsupportedError(message)
    472        self.unsupported_messages.append(message)

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

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

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

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

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

    480    def pad_comment(self, comment: str) -> str:
    481        comment = " " + comment if comment[0].strip() else comment
    482        comment = comment + " " if comment[-1].strip() else comment
    483        return comment

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

    485    def maybe_comment(
    486        self,
    487        sql: str,
    488        expression: t.Optional[exp.Expression] = None,
    489        comments: t.Optional[t.List[str]] = None,
    490    ) -> str:
    491        comments = (
    492            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    493            if self.comments
    494            else None
    495        )
    496
    497        if not comments or isinstance(expression, exp.Binary):
    498            return sql
    499
    500        comments_sql = " ".join(
    501            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    502        )
    503
    504        if not comments_sql:
    505            return sql
    506
    507        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    508            return (
    509                f"{self.sep()}{comments_sql}{sql}"
    510                if sql[0].isspace()
    511                else f"{comments_sql}{self.sep()}{sql}"
    512            )
    513
    514        return f"{sql} {comments_sql}"

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

    516    def wrap(self, expression: exp.Expression | str) -> str:
    517        this_sql = self.indent(
    518            self.sql(expression)
    519            if isinstance(expression, (exp.Select, exp.Union))
    520            else self.sql(expression, "this"),
    521            level=1,
    522            pad=0,
    523        )
    524        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    526    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    527        original = self.identify
    528        self.identify = False
    529        result = func(*args, **kwargs)
    530        self.identify = original
    531        return result

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

    533    def normalize_func(self, name: str) -> str:
    534        if self.normalize_functions == "upper" or self.normalize_functions is True:
    535            return name.upper()
    536        if self.normalize_functions == "lower":
    537            return name.lower()
    538        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

    540    def indent(
    541        self,
    542        sql: str,
    543        level: int = 0,
    544        pad: t.Optional[int] = None,
    545        skip_first: bool = False,
    546        skip_last: bool = False,
    547    ) -> str:
    548        if not self.pretty:
    549            return sql
    550
    551        pad = self.pad if pad is None else pad
    552        lines = sql.split("\n")
    553
    554        return "\n".join(
    555            line
    556            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    557            else f"{' ' * (level * self._indent + pad)}{line}"
    558            for i, line in enumerate(lines)
    559        )

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

    561    def sql(
    562        self,
    563        expression: t.Optional[str | exp.Expression],
    564        key: t.Optional[str] = None,
    565        comment: bool = True,
    566    ) -> str:
    567        if not expression:
    568            return ""
    569
    570        if isinstance(expression, str):
    571            return expression
    572
    573        if key:
    574            value = expression.args.get(key)
    575            if value:
    576                return self.sql(value)
    577            return ""
    578
    579        if self._cache is not None:
    580            expression_id = hash(expression)
    581
    582            if expression_id in self._cache:
    583                return self._cache[expression_id]
    584
    585        transform = self.TRANSFORMS.get(expression.__class__)
    586
    587        if callable(transform):
    588            sql = transform(self, expression)
    589        elif transform:
    590            sql = transform
    591        elif isinstance(expression, exp.Expression):
    592            exp_handler_name = f"{expression.key}_sql"
    593
    594            if hasattr(self, exp_handler_name):
    595                sql = getattr(self, exp_handler_name)(expression)
    596            elif isinstance(expression, exp.Func):
    597                sql = self.function_fallback_sql(expression)
    598            elif isinstance(expression, exp.Property):
    599                sql = self.property_sql(expression)
    600            else:
    601                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    602        else:
    603            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    604
    605        sql = self.maybe_comment(sql, expression) if self.comments and comment else sql
    606
    607        if self._cache is not None:
    608            self._cache[expression_id] = sql
    609        return sql

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

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

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

    616    def cache_sql(self, expression: exp.Cache) -> str:
    617        lazy = " LAZY" if expression.args.get("lazy") else ""
    618        table = self.sql(expression, "this")
    619        options = expression.args.get("options")
    620        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    621        sql = self.sql(expression, "expression")
    622        sql = f" AS{self.sep()}{sql}" if sql else ""
    623        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    624        return self.prepend_ctes(expression, sql)

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

    626    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    627        if isinstance(expression.parent, exp.Cast):
    628            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    629        default = "DEFAULT " if expression.args.get("default") else ""
    630        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    632    def column_sql(self, expression: exp.Column) -> str:
    633        join_mark = " (+)" if expression.args.get("join_mark") else ""
    634
    635        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
    636            join_mark = ""
    637            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    638
    639        column = ".".join(
    640            self.sql(part)
    641            for part in (
    642                expression.args.get("catalog"),
    643                expression.args.get("db"),
    644                expression.args.get("table"),
    645                expression.args.get("this"),
    646            )
    647            if part
    648        )
    649
    650        return f"{column}{join_mark}"

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

    652    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    653        this = self.sql(expression, "this")
    654        this = f" {this}" if this else ""
    655        position = self.sql(expression, "position")
    656        return f"{position}{this}"

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

    658    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    659        column = self.sql(expression, "this")
    660        kind = self.sql(expression, "kind")
    661        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    662        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    663        kind = f"{sep}{kind}" if kind else ""
    664        constraints = f" {constraints}" if constraints else ""
    665        position = self.sql(expression, "position")
    666        position = f" {position}" if position else ""
    667
    668        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    669            kind = ""
    670
    671        return f"{exists}{column}{kind}{constraints}{position}"

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

    673    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    674        this = self.sql(expression, "this")
    675        kind_sql = self.sql(expression, "kind").strip()
    676        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    678    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    679        this = self.sql(expression, "this")
    680        if expression.args.get("not_null"):
    681            persisted = " PERSISTED NOT NULL"
    682        elif expression.args.get("persisted"):
    683            persisted = " PERSISTED"
    684        else:
    685            persisted = ""
    686        return f"AS {this}{persisted}"

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

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

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

    691    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    692        if isinstance(expression.this, list):
    693            this = self.wrap(self.expressions(expression, key="this", flat=True))
    694        else:
    695            this = self.sql(expression, "this")
    696
    697        return f"COMPRESS {this}"

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

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

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

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

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

    735    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    736        desc = expression.args.get("desc")
    737        if desc is not None:
    738            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    739        return f"PRIMARY KEY"

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

    741    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    742        this = self.sql(expression, "this")
    743        this = f" {this}" if this else ""
    744        index_type = expression.args.get("index_type")
    745        index_type = f" USING {index_type}" if index_type else ""
    746        return f"UNIQUE{this}{index_type}"

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

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

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

    751    def create_sql(self, expression: exp.Create) -> str:
    752        kind = self.sql(expression, "kind").upper()
    753        properties = expression.args.get("properties")
    754        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    755
    756        this = self.createable_sql(expression, properties_locs)
    757
    758        properties_sql = ""
    759        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    760            exp.Properties.Location.POST_WITH
    761        ):
    762            properties_sql = self.sql(
    763                exp.Properties(
    764                    expressions=[
    765                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    766                        *properties_locs[exp.Properties.Location.POST_WITH],
    767                    ]
    768                )
    769            )
    770
    771        begin = " BEGIN" if expression.args.get("begin") else ""
    772        expression_sql = self.sql(expression, "expression")
    773        if expression_sql:
    774            expression_sql = f"{begin}{self.sep()}{expression_sql}"
    775
    776            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    777                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    778                    postalias_props_sql = self.properties(
    779                        exp.Properties(
    780                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    781                        ),
    782                        wrapped=False,
    783                    )
    784                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
    785                else:
    786                    expression_sql = f" AS{expression_sql}"
    787
    788        postindex_props_sql = ""
    789        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    790            postindex_props_sql = self.properties(
    791                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    792                wrapped=False,
    793                prefix=" ",
    794            )
    795
    796        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    797        indexes = f" {indexes}" if indexes else ""
    798        index_sql = indexes + postindex_props_sql
    799
    800        replace = " OR REPLACE" if expression.args.get("replace") else ""
    801        unique = " UNIQUE" if expression.args.get("unique") else ""
    802
    803        postcreate_props_sql = ""
    804        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    805            postcreate_props_sql = self.properties(
    806                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    807                sep=" ",
    808                prefix=" ",
    809                wrapped=False,
    810            )
    811
    812        modifiers = "".join((replace, unique, postcreate_props_sql))
    813
    814        postexpression_props_sql = ""
    815        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    816            postexpression_props_sql = self.properties(
    817                exp.Properties(
    818                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    819                ),
    820                sep=" ",
    821                prefix=" ",
    822                wrapped=False,
    823            )
    824
    825        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    826        no_schema_binding = (
    827            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    828        )
    829
    830        clone = self.sql(expression, "clone")
    831        clone = f" {clone}" if clone else ""
    832
    833        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    834        return self.prepend_ctes(expression, expression_sql)

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

    836    def clone_sql(self, expression: exp.Clone) -> str:
    837        this = self.sql(expression, "this")
    838        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    839        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    840        this = f"{shallow}{keyword} {this}"
    841        when = self.sql(expression, "when")
    842
    843        if when:
    844            kind = self.sql(expression, "kind")
    845            expr = self.sql(expression, "expression")
    846            return f"{this} {when} ({kind} => {expr})"
    847
    848        return this

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

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

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

    853    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    854        with_ = self.sql(expression, "with")
    855        if with_:
    856            sql = f"{with_}{self.sep()}{sql}"
    857        return sql

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

    859    def with_sql(self, expression: exp.With) -> str:
    860        sql = self.expressions(expression, flat=True)
    861        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
    862
    863        return f"WITH {recursive}{sql}"

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

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

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

    869    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    870        alias = self.sql(expression, "this")
    871        columns = self.expressions(expression, key="columns", flat=True)
    872        columns = f"({columns})" if columns else ""
    873        return f"{alias}{columns}"

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

    875    def bitstring_sql(self, expression: exp.BitString) -> str:
    876        this = self.sql(expression, "this")
    877        if self.BIT_START:
    878            return f"{self.BIT_START}{this}{self.BIT_END}"
    879        return f"{int(this, 2)}"

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

    881    def hexstring_sql(self, expression: exp.HexString) -> str:
    882        this = self.sql(expression, "this")
    883        if self.HEX_START:
    884            return f"{self.HEX_START}{this}{self.HEX_END}"
    885        return f"{int(this, 16)}"

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

    887    def bytestring_sql(self, expression: exp.ByteString) -> str:
    888        this = self.sql(expression, "this")
    889        if self.BYTE_START:
    890            return f"{self.BYTE_START}{this}{self.BYTE_END}"
    891        return this

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

    893    def rawstring_sql(self, expression: exp.RawString) -> str:
    894        string = self.escape_str(expression.this.replace("\\", "\\\\"))
    895        return f"{self.QUOTE_START}{string}{self.QUOTE_END}"

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

    897    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    898        this = self.sql(expression, "this")
    899        specifier = self.sql(expression, "expression")
    900        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    901        return f"{this}{specifier}"

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

    903    def datatype_sql(self, expression: exp.DataType) -> str:
    904        type_value = expression.this
    905
    906        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    907            type_sql = self.sql(expression, "kind")
    908        else:
    909            type_sql = (
    910                self.TYPE_MAPPING.get(type_value, type_value.value)
    911                if isinstance(type_value, exp.DataType.Type)
    912                else type_value
    913            )
    914
    915        nested = ""
    916        interior = self.expressions(expression, flat=True)
    917        values = ""
    918
    919        if interior:
    920            if expression.args.get("nested"):
    921                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    922                if expression.args.get("values") is not None:
    923                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    924                    values = self.expressions(expression, key="values", flat=True)
    925                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    926            elif type_value == exp.DataType.Type.INTERVAL:
    927                nested = f" {interior}"
    928            else:
    929                nested = f"({interior})"
    930
    931        type_sql = f"{type_sql}{nested}{values}"
    932        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    933            exp.DataType.Type.TIMETZ,
    934            exp.DataType.Type.TIMESTAMPTZ,
    935        ):
    936            type_sql = f"{type_sql} WITH TIME ZONE"
    937
    938        return type_sql

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

    940    def directory_sql(self, expression: exp.Directory) -> str:
    941        local = "LOCAL " if expression.args.get("local") else ""
    942        row_format = self.sql(expression, "row_format")
    943        row_format = f" {row_format}" if row_format else ""
    944        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    946    def delete_sql(self, expression: exp.Delete) -> str:
    947        this = self.sql(expression, "this")
    948        this = f" FROM {this}" if this else ""
    949        using = self.sql(expression, "using")
    950        using = f" USING {using}" if using else ""
    951        where = self.sql(expression, "where")
    952        returning = self.sql(expression, "returning")
    953        limit = self.sql(expression, "limit")
    954        tables = self.expressions(expression, key="tables")
    955        tables = f" {tables}" if tables else ""
    956        if self.RETURNING_END:
    957            expression_sql = f"{this}{using}{where}{returning}{limit}"
    958        else:
    959            expression_sql = f"{returning}{this}{using}{where}{limit}"
    960        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    962    def drop_sql(self, expression: exp.Drop) -> str:
    963        this = self.sql(expression, "this")
    964        kind = expression.args["kind"]
    965        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    966        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    967        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    968        cascade = " CASCADE" if expression.args.get("cascade") else ""
    969        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    970        purge = " PURGE" if expression.args.get("purge") else ""
    971        return (
    972            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
    973        )

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

    975    def except_sql(self, expression: exp.Except) -> str:
    976        return self.prepend_ctes(
    977            expression,
    978            self.set_operation(expression, self.except_op(expression)),
    979        )

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

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

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

    984    def fetch_sql(self, expression: exp.Fetch) -> str:
    985        direction = expression.args.get("direction")
    986        direction = f" {direction.upper()}" if direction else ""
    987        count = expression.args.get("count")
    988        count = f" {count}" if count else ""
    989        if expression.args.get("percent"):
    990            count = f"{count} PERCENT"
    991        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    992        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

     994    def filter_sql(self, expression: exp.Filter) -> str:
     995        if self.AGGREGATE_FILTER_SUPPORTED:
     996            this = self.sql(expression, "this")
     997            where = self.sql(expression, "expression").strip()
     998            return f"{this} FILTER({where})"
     999
    1000        agg = expression.this.copy()
    1001        agg_arg = agg.this
    1002        cond = expression.expression.this
    1003        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1004        return self.sql(agg)

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

    1006    def hint_sql(self, expression: exp.Hint) -> str:
    1007        if not self.QUERY_HINTS:
    1008            self.unsupported("Hints are not supported")
    1009            return ""
    1010
    1011        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1013    def index_sql(self, expression: exp.Index) -> str:
    1014        unique = "UNIQUE " if expression.args.get("unique") else ""
    1015        primary = "PRIMARY " if expression.args.get("primary") else ""
    1016        amp = "AMP " if expression.args.get("amp") else ""
    1017        name = self.sql(expression, "this")
    1018        name = f"{name} " if name else ""
    1019        table = self.sql(expression, "table")
    1020        table = f"{self.INDEX_ON} {table}" if table else ""
    1021        using = self.sql(expression, "using")
    1022        using = f" USING {using}" if using else ""
    1023        index = "INDEX " if not table else ""
    1024        columns = self.expressions(expression, key="columns", flat=True)
    1025        columns = f"({columns})" if columns else ""
    1026        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1027        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1028        where = self.sql(expression, "where")
    1029        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"

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

    1031    def identifier_sql(self, expression: exp.Identifier) -> str:
    1032        text = expression.name
    1033        lower = text.lower()
    1034        text = lower if self.normalize and not expression.quoted else text
    1035        text = text.replace(self.IDENTIFIER_END, self._escaped_identifier_end)
    1036        if (
    1037            expression.quoted
    1038            or self.can_identify(text, self.identify)
    1039            or lower in self.RESERVED_KEYWORDS
    1040            or (not self.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1041        ):
    1042            text = f"{self.IDENTIFIER_START}{text}{self.IDENTIFIER_END}"
    1043        return text

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

    1045    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1046        input_format = self.sql(expression, "input_format")
    1047        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1048        output_format = self.sql(expression, "output_format")
    1049        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1050        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1059    def properties_sql(self, expression: exp.Properties) -> str:
    1060        root_properties = []
    1061        with_properties = []
    1062
    1063        for p in expression.expressions:
    1064            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1065            if p_loc == exp.Properties.Location.POST_WITH:
    1066                with_properties.append(p.copy())
    1067            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1068                root_properties.append(p.copy())
    1069
    1070        return self.root_properties(
    1071            exp.Properties(expressions=root_properties)
    1072        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    1074    def root_properties(self, properties: exp.Properties) -> str:
    1075        if properties.expressions:
    1076            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1077        return ""

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

    1079    def properties(
    1080        self,
    1081        properties: exp.Properties,
    1082        prefix: str = "",
    1083        sep: str = ", ",
    1084        suffix: str = "",
    1085        wrapped: bool = True,
    1086    ) -> str:
    1087        if properties.expressions:
    1088            expressions = self.expressions(properties, sep=sep, indent=False)
    1089            if expressions:
    1090                expressions = self.wrap(expressions) if wrapped else expressions
    1091                return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
    1092        return ""

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

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

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

    1097    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1098        properties_locs = defaultdict(list)
    1099        for p in properties.expressions:
    1100            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1101            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1102                properties_locs[p_loc].append(p.copy())
    1103            else:
    1104                self.unsupported(f"Unsupported property {p.key}")
    1105
    1106        return properties_locs

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

    1108    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1109        if isinstance(expression.this, exp.Dot):
    1110            return self.sql(expression, "this")
    1111        return f"'{expression.name}'" if string_key else expression.name

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

    1113    def property_sql(self, expression: exp.Property) -> str:
    1114        property_cls = expression.__class__
    1115        if property_cls == exp.Property:
    1116            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1117
    1118        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1119        if not property_name:
    1120            self.unsupported(f"Unsupported property {expression.key}")
    1121
    1122        return f"{property_name}={self.sql(expression, 'this')}"

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

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

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

    1129    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1130        no = "NO " if expression.args.get("no") else ""
    1131        protection = " PROTECTION" if expression.args.get("protection") else ""
    1132        return f"{no}FALLBACK{protection}"

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

    1134    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1135        no = "NO " if expression.args.get("no") else ""
    1136        local = expression.args.get("local")
    1137        local = f"{local} " if local else ""
    1138        dual = "DUAL " if expression.args.get("dual") else ""
    1139        before = "BEFORE " if expression.args.get("before") else ""
    1140        after = "AFTER " if expression.args.get("after") else ""
    1141        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1143    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1144        freespace = self.sql(expression, "this")
    1145        percent = " PERCENT" if expression.args.get("percent") else ""
    1146        return f"FREESPACE={freespace}{percent}"

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

    1148    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1149        if expression.args.get("default"):
    1150            property = "DEFAULT"
    1151        elif expression.args.get("on"):
    1152            property = "ON"
    1153        else:
    1154            property = "OFF"
    1155        return f"CHECKSUM={property}"

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

    1157    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1158        if expression.args.get("no"):
    1159            return "NO MERGEBLOCKRATIO"
    1160        if expression.args.get("default"):
    1161            return "DEFAULT MERGEBLOCKRATIO"
    1162
    1163        percent = " PERCENT" if expression.args.get("percent") else ""
    1164        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1166    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1167        default = expression.args.get("default")
    1168        minimum = expression.args.get("minimum")
    1169        maximum = expression.args.get("maximum")
    1170        if default or minimum or maximum:
    1171            if default:
    1172                prop = "DEFAULT"
    1173            elif minimum:
    1174                prop = "MINIMUM"
    1175            else:
    1176                prop = "MAXIMUM"
    1177            return f"{prop} DATABLOCKSIZE"
    1178        units = expression.args.get("units")
    1179        units = f" {units}" if units else ""
    1180        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1182    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1183        autotemp = expression.args.get("autotemp")
    1184        always = expression.args.get("always")
    1185        default = expression.args.get("default")
    1186        manual = expression.args.get("manual")
    1187        never = expression.args.get("never")
    1188
    1189        if autotemp is not None:
    1190            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1191        elif always:
    1192            prop = "ALWAYS"
    1193        elif default:
    1194            prop = "DEFAULT"
    1195        elif manual:
    1196            prop = "MANUAL"
    1197        elif never:
    1198            prop = "NEVER"
    1199        return f"BLOCKCOMPRESSION={prop}"

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

    1201    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1202        no = expression.args.get("no")
    1203        no = " NO" if no else ""
    1204        concurrent = expression.args.get("concurrent")
    1205        concurrent = " CONCURRENT" if concurrent else ""
    1206
    1207        for_ = ""
    1208        if expression.args.get("for_all"):
    1209            for_ = " FOR ALL"
    1210        elif expression.args.get("for_insert"):
    1211            for_ = " FOR INSERT"
    1212        elif expression.args.get("for_none"):
    1213            for_ = " FOR NONE"
    1214        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

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

    1216    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1217        kind = expression.args.get("kind")
    1218        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1219        for_or_in = expression.args.get("for_or_in")
    1220        lock_type = expression.args.get("lock_type")
    1221        override = " OVERRIDE" if expression.args.get("override") else ""
    1222        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"

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

    1224    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1225        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1226        statistics = expression.args.get("statistics")
    1227        statistics_sql = ""
    1228        if statistics is not None:
    1229            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1230        return f"{data_sql}{statistics_sql}"

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

    1232    def insert_sql(self, expression: exp.Insert) -> str:
    1233        overwrite = expression.args.get("overwrite")
    1234
    1235        if isinstance(expression.this, exp.Directory):
    1236            this = " OVERWRITE" if overwrite else " INTO"
    1237        else:
    1238            this = " OVERWRITE TABLE" if overwrite else " INTO"
    1239
    1240        alternative = expression.args.get("alternative")
    1241        alternative = f" OR {alternative}" if alternative else ""
    1242        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1243
    1244        this = f"{this} {self.sql(expression, 'this')}"
    1245
    1246        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1247        partition_sql = (
    1248            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1249        )
    1250        where = self.sql(expression, "where")
    1251        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1252        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1253        conflict = self.sql(expression, "conflict")
    1254        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1255        returning = self.sql(expression, "returning")
    1256
    1257        if self.RETURNING_END:
    1258            expression_sql = f"{expression_sql}{conflict}{returning}"
    1259        else:
    1260            expression_sql = f"{returning}{expression_sql}{conflict}"
    1261
    1262        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1263        return self.prepend_ctes(expression, sql)

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

    1265    def intersect_sql(self, expression: exp.Intersect) -> str:
    1266        return self.prepend_ctes(
    1267            expression,
    1268            self.set_operation(expression, self.intersect_op(expression)),
    1269        )

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

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

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

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

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

    1277    def kill_sql(self, expression: exp.Kill) -> str:
    1278        kind = self.sql(expression, "kind")
    1279        kind = f" {kind}" if kind else ""
    1280        this = self.sql(expression, "this")
    1281        this = f" {this}" if this else ""
    1282        return f"KILL{kind}{this}"

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

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

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

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

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

    1290    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1291        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1292        constraint = self.sql(expression, "constraint")
    1293        if constraint:
    1294            constraint = f"ON CONSTRAINT {constraint}"
    1295        key = self.expressions(expression, key="key", flat=True)
    1296        do = "" if expression.args.get("duplicate") else " DO "
    1297        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1298        expressions = self.expressions(expression, flat=True)
    1299        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1300        if expressions:
    1301            expressions = f"UPDATE {set_keyword}{expressions}"
    1302        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"

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

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

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

    1307    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1308        fields = expression.args.get("fields")
    1309        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1310        escaped = expression.args.get("escaped")
    1311        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1312        items = expression.args.get("collection_items")
    1313        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1314        keys = expression.args.get("map_keys")
    1315        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1316        lines = expression.args.get("lines")
    1317        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1318        null = expression.args.get("null")
    1319        null = f" NULL DEFINED AS {null}" if null else ""
    1320        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1325    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1326        this = f"{self.sql(expression, 'this')} INDEX"
    1327        target = self.sql(expression, "target")
    1328        target = f" FOR {target}" if target else ""
    1329        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1331    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1332        table = ".".join(
    1333            part
    1334            for part in [
    1335                self.sql(expression, "catalog"),
    1336                self.sql(expression, "db"),
    1337                self.sql(expression, "this"),
    1338            ]
    1339            if part
    1340        )
    1341
    1342        version = self.sql(expression, "version")
    1343        version = f" {version}" if version else ""
    1344        alias = self.sql(expression, "alias")
    1345        alias = f"{sep}{alias}" if alias else ""
    1346        hints = self.expressions(expression, key="hints", sep=" ")
    1347        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1348        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1349        pivots = f" {pivots}" if pivots else ""
    1350        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
    1351        laterals = self.expressions(expression, key="laterals", sep="")
    1352        file_format = self.sql(expression, "format")
    1353        if file_format:
    1354            pattern = self.sql(expression, "pattern")
    1355            pattern = f", PATTERN => {pattern}" if pattern else ""
    1356            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1357
    1358        return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}"

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

    1360    def tablesample_sql(
    1361        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1362    ) -> str:
    1363        if self.ALIAS_POST_TABLESAMPLE and expression.this.alias:
    1364            table = expression.this.copy()
    1365            table.set("alias", None)
    1366            this = self.sql(table)
    1367            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1368        else:
    1369            this = self.sql(expression, "this")
    1370            alias = ""
    1371
    1372        method = self.sql(expression, "method")
    1373        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1374        numerator = self.sql(expression, "bucket_numerator")
    1375        denominator = self.sql(expression, "bucket_denominator")
    1376        field = self.sql(expression, "bucket_field")
    1377        field = f" ON {field}" if field else ""
    1378        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1379        percent = self.sql(expression, "percent")
    1380        percent = f"{percent} PERCENT" if percent else ""
    1381        rows = self.sql(expression, "rows")
    1382        rows = f"{rows} ROWS" if rows else ""
    1383
    1384        size = self.sql(expression, "size")
    1385        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1386            size = f"{size} PERCENT"
    1387
    1388        seed = self.sql(expression, "seed")
    1389        seed = f" {seed_prefix} ({seed})" if seed else ""
    1390        kind = expression.args.get("kind", "TABLESAMPLE")
    1391
    1392        expr = f"{bucket}{percent}{rows}{size}"
    1393        if self.TABLESAMPLE_REQUIRES_PARENS:
    1394            expr = f"({expr})"
    1395
    1396        return f"{this} {kind} {method}{expr}{seed}{alias}"

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

    1398    def pivot_sql(self, expression: exp.Pivot) -> str:
    1399        expressions = self.expressions(expression, flat=True)
    1400
    1401        if expression.this:
    1402            this = self.sql(expression, "this")
    1403            on = f"{self.seg('ON')} {expressions}"
    1404            using = self.expressions(expression, key="using", flat=True)
    1405            using = f"{self.seg('USING')} {using}" if using else ""
    1406            group = self.sql(expression, "group")
    1407            return f"PIVOT {this}{on}{using}{group}"
    1408
    1409        alias = self.sql(expression, "alias")
    1410        alias = f" AS {alias}" if alias else ""
    1411        unpivot = expression.args.get("unpivot")
    1412        direction = "UNPIVOT" if unpivot else "PIVOT"
    1413        field = self.sql(expression, "field")
    1414        include_nulls = expression.args.get("include_nulls")
    1415        if include_nulls is not None:
    1416            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1417        else:
    1418            nulls = ""
    1419        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1421    def version_sql(self, expression: exp.Version) -> str:
    1422        this = f"FOR {expression.name}"
    1423        kind = expression.text("kind")
    1424        expr = self.sql(expression, "expression")
    1425        return f"{this} {kind} {expr}"

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

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

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

    1430    def update_sql(self, expression: exp.Update) -> str:
    1431        this = self.sql(expression, "this")
    1432        set_sql = self.expressions(expression, flat=True)
    1433        from_sql = self.sql(expression, "from")
    1434        where_sql = self.sql(expression, "where")
    1435        returning = self.sql(expression, "returning")
    1436        order = self.sql(expression, "order")
    1437        limit = self.sql(expression, "limit")
    1438        if self.RETURNING_END:
    1439            expression_sql = f"{from_sql}{where_sql}{returning}"
    1440        else:
    1441            expression_sql = f"{returning}{from_sql}{where_sql}"
    1442        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1443        return self.prepend_ctes(expression, sql)

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

    1445    def values_sql(self, expression: exp.Values) -> str:
    1446        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1447        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
    1448            args = self.expressions(expression)
    1449            alias = self.sql(expression, "alias")
    1450            values = f"VALUES{self.seg('')}{args}"
    1451            values = (
    1452                f"({values})"
    1453                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1454                else values
    1455            )
    1456            return f"{values} AS {alias}" if alias else values
    1457
    1458        # Converts `VALUES...` expression into a series of select unions.
    1459        expression = expression.copy()
    1460        alias_node = expression.args.get("alias")
    1461        column_names = alias_node and alias_node.columns
    1462
    1463        selects: t.List[exp.Subqueryable] = []
    1464
    1465        for i, tup in enumerate(expression.expressions):
    1466            row = tup.expressions
    1467
    1468            if i == 0 and column_names:
    1469                row = [
    1470                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1471                ]
    1472
    1473            selects.append(exp.Select(expressions=row))
    1474
    1475        if self.pretty:
    1476            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1477            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1478            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1479            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1480            return self.subquery_sql(
    1481                subqueryable.subquery(alias_node and alias_node.this, copy=False)
    1482            )
    1483
    1484        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1485        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1486        return f"({unions}){alias}"

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

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

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

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

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

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

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

    1499    def group_sql(self, expression: exp.Group) -> str:
    1500        group_by = self.op_expressions("GROUP BY", expression)
    1501
    1502        if expression.args.get("all"):
    1503            return f"{group_by} ALL"
    1504
    1505        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1506        grouping_sets = (
    1507            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1508        )
    1509
    1510        cube = expression.args.get("cube", [])
    1511        if seq_get(cube, 0) is True:
    1512            return f"{group_by}{self.seg('WITH CUBE')}"
    1513        else:
    1514            cube_sql = self.expressions(expression, key="cube", indent=False)
    1515            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1516
    1517        rollup = expression.args.get("rollup", [])
    1518        if seq_get(rollup, 0) is True:
    1519            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1520        else:
    1521            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1522            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1523
    1524        groupings = csv(
    1525            grouping_sets,
    1526            cube_sql,
    1527            rollup_sql,
    1528            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1529            sep=self.GROUPINGS_SEP,
    1530        )
    1531
    1532        if expression.args.get("expressions") and groupings:
    1533            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1534
    1535        return f"{group_by}{groupings}"

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

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

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

    1541    def connect_sql(self, expression: exp.Connect) -> str:
    1542        start = self.sql(expression, "start")
    1543        start = self.seg(f"START WITH {start}") if start else ""
    1544        connect = self.sql(expression, "connect")
    1545        connect = self.seg(f"CONNECT BY {connect}")
    1546        return start + connect

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

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

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

    1551    def join_sql(self, expression: exp.Join) -> str:
    1552        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1553            side = None
    1554        else:
    1555            side = expression.side
    1556
    1557        op_sql = " ".join(
    1558            op
    1559            for op in (
    1560                expression.method,
    1561                "GLOBAL" if expression.args.get("global") else None,
    1562                side,
    1563                expression.kind,
    1564                expression.hint if self.JOIN_HINTS else None,
    1565            )
    1566            if op
    1567        )
    1568        on_sql = self.sql(expression, "on")
    1569        using = expression.args.get("using")
    1570
    1571        if not on_sql and using:
    1572            on_sql = csv(*(self.sql(column) for column in using))
    1573
    1574        this_sql = self.sql(expression, "this")
    1575
    1576        if on_sql:
    1577            on_sql = self.indent(on_sql, skip_first=True)
    1578            space = self.seg(" " * self.pad) if self.pretty else " "
    1579            if using:
    1580                on_sql = f"{space}USING ({on_sql})"
    1581            else:
    1582                on_sql = f"{space}ON {on_sql}"
    1583        elif not op_sql:
    1584            return f", {this_sql}"
    1585
    1586        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1587        return f"{self.seg(op_sql)} {this_sql}{on_sql}"

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

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

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

    1594    def lateral_sql(self, expression: exp.Lateral) -> str:
    1595        this = self.sql(expression, "this")
    1596
    1597        if isinstance(expression.this, exp.Subquery):
    1598            return f"LATERAL {this}"
    1599
    1600        if expression.args.get("view"):
    1601            alias = expression.args["alias"]
    1602            columns = self.expressions(alias, key="columns", flat=True)
    1603            table = f" {alias.name}" if alias.name else ""
    1604            columns = f" AS {columns}" if columns else ""
    1605            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1606            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1607
    1608        alias = self.sql(expression, "alias")
    1609        alias = f" AS {alias}" if alias else ""
    1610        return f"LATERAL {this}{alias}"

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

    1612    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1613        this = self.sql(expression, "this")
    1614        args = ", ".join(
    1615            sql
    1616            for sql in (
    1617                self.sql(expression, "offset"),
    1618                self.sql(expression, "expression"),
    1619            )
    1620            if sql
    1621        )
    1622        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args}"

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

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

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

    1628    def setitem_sql(self, expression: exp.SetItem) -> str:
    1629        kind = self.sql(expression, "kind")
    1630        kind = f"{kind} " if kind else ""
    1631        this = self.sql(expression, "this")
    1632        expressions = self.expressions(expression)
    1633        collate = self.sql(expression, "collate")
    1634        collate = f" COLLATE {collate}" if collate else ""
    1635        global_ = "GLOBAL " if expression.args.get("global") else ""
    1636        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1638    def set_sql(self, expression: exp.Set) -> str:
    1639        expressions = (
    1640            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1641        )
    1642        tag = " TAG" if expression.args.get("tag") else ""
    1643        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    1648    def lock_sql(self, expression: exp.Lock) -> str:
    1649        if not self.LOCKING_READS_SUPPORTED:
    1650            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1651            return ""
    1652
    1653        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1654        expressions = self.expressions(expression, flat=True)
    1655        expressions = f" OF {expressions}" if expressions else ""
    1656        wait = expression.args.get("wait")
    1657
    1658        if wait is not None:
    1659            if isinstance(wait, exp.Literal):
    1660                wait = f" WAIT {self.sql(wait)}"
    1661            else:
    1662                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1663
    1664        return f"{lock_type}{expressions}{wait or ''}"

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

    1666    def literal_sql(self, expression: exp.Literal) -> str:
    1667        text = expression.this or ""
    1668        if expression.is_string:
    1669            text = f"{self.QUOTE_START}{self.escape_str(text)}{self.QUOTE_END}"
    1670        return text

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

    1672    def escape_str(self, text: str) -> str:
    1673        text = text.replace(self.QUOTE_END, self._escaped_quote_end)
    1674        if self.INVERSE_ESCAPE_SEQUENCES:
    1675            text = "".join(self.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1676        elif self.pretty:
    1677            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1678        return text

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

    1680    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1681        local = " LOCAL" if expression.args.get("local") else ""
    1682        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1683        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1684        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1685        partition = self.sql(expression, "partition")
    1686        partition = f" {partition}" if partition else ""
    1687        input_format = self.sql(expression, "input_format")
    1688        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1689        serde = self.sql(expression, "serde")
    1690        serde = f" SERDE {serde}" if serde else ""
    1691        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1693    def null_sql(self, *_) -> str:
    1694        return "NULL"

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

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

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

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

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

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

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

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

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

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

    1713    def ordered_sql(self, expression: exp.Ordered) -> str:
    1714        desc = expression.args.get("desc")
    1715        asc = not desc
    1716
    1717        nulls_first = expression.args.get("nulls_first")
    1718        nulls_last = not nulls_first
    1719        nulls_are_large = self.NULL_ORDERING == "nulls_are_large"
    1720        nulls_are_small = self.NULL_ORDERING == "nulls_are_small"
    1721        nulls_are_last = self.NULL_ORDERING == "nulls_are_last"
    1722
    1723        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    1724        nulls_sort_change = ""
    1725        if nulls_first and (
    1726            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1727        ):
    1728            nulls_sort_change = " NULLS FIRST"
    1729        elif (
    1730            nulls_last
    1731            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1732            and not nulls_are_last
    1733        ):
    1734            nulls_sort_change = " NULLS LAST"
    1735
    1736        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1737            self.unsupported(
    1738                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1739            )
    1740            nulls_sort_change = ""
    1741
    1742        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

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

    1744    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1745        partition = self.partition_by_sql(expression)
    1746        order = self.sql(expression, "order")
    1747        measures = self.expressions(expression, key="measures")
    1748        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1749        rows = self.sql(expression, "rows")
    1750        rows = self.seg(rows) if rows else ""
    1751        after = self.sql(expression, "after")
    1752        after = self.seg(after) if after else ""
    1753        pattern = self.sql(expression, "pattern")
    1754        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1755        definition_sqls = [
    1756            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1757            for definition in expression.args.get("define", [])
    1758        ]
    1759        definitions = self.expressions(sqls=definition_sqls)
    1760        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1761        body = "".join(
    1762            (
    1763                partition,
    1764                order,
    1765                measures,
    1766                rows,
    1767                after,
    1768                pattern,
    1769                define,
    1770            )
    1771        )
    1772        alias = self.sql(expression, "alias")
    1773        alias = f" {alias}" if alias else ""
    1774        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    1776    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1777        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
    1778
    1779        # If the limit is generated as TOP, we need to ensure it's not generated twice
    1780        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
    1781
    1782        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1783            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    1784        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1785            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    1786
    1787        fetch = isinstance(limit, exp.Fetch)
    1788
    1789        offset_limit_modifiers = (
    1790            self.offset_limit_modifiers(expression, fetch, limit)
    1791            if with_offset_limit_modifiers
    1792            else []
    1793        )
    1794
    1795        return csv(
    1796            *sqls,
    1797            *[self.sql(join) for join in expression.args.get("joins") or []],
    1798            self.sql(expression, "connect"),
    1799            self.sql(expression, "match"),
    1800            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1801            self.sql(expression, "where"),
    1802            self.sql(expression, "group"),
    1803            self.sql(expression, "having"),
    1804            *self.after_having_modifiers(expression),
    1805            self.sql(expression, "order"),
    1806            *offset_limit_modifiers,
    1807            *self.after_limit_modifiers(expression),
    1808            sep="",
    1809        )

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

    1811    def offset_limit_modifiers(
    1812        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    1813    ) -> t.List[str]:
    1814        return [
    1815            self.sql(expression, "offset") if fetch else self.sql(limit),
    1816            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1817        ]

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

    1819    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1820        return [
    1821            self.sql(expression, "qualify"),
    1822            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1823            if expression.args.get("windows")
    1824            else "",
    1825            self.sql(expression, "distribute"),
    1826            self.sql(expression, "sort"),
    1827            self.sql(expression, "cluster"),
    1828        ]

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

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

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

    1835    def select_sql(self, expression: exp.Select) -> str:
    1836        hint = self.sql(expression, "hint")
    1837        distinct = self.sql(expression, "distinct")
    1838        distinct = f" {distinct}" if distinct else ""
    1839        kind = self.sql(expression, "kind").upper()
    1840        limit = expression.args.get("limit")
    1841        top = (
    1842            self.limit_sql(limit, top=True)
    1843            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
    1844            else ""
    1845        )
    1846
    1847        expressions = self.expressions(expression)
    1848
    1849        if kind:
    1850            if kind in self.SELECT_KINDS:
    1851                kind = f" AS {kind}"
    1852            else:
    1853                if kind == "STRUCT":
    1854                    expressions = self.expressions(
    1855                        sqls=[
    1856                            self.sql(
    1857                                exp.Struct(
    1858                                    expressions=[
    1859                                        exp.column(e.output_name).eq(
    1860                                            e.this if isinstance(e, exp.Alias) else e
    1861                                        )
    1862                                        for e in expression.expressions
    1863                                    ]
    1864                                )
    1865                            )
    1866                        ]
    1867                    )
    1868                kind = ""
    1869
    1870        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1871        sql = self.query_modifiers(
    1872            expression,
    1873            f"SELECT{top}{hint}{distinct}{kind}{expressions}",
    1874            self.sql(expression, "into", comment=False),
    1875            self.sql(expression, "from", comment=False),
    1876        )
    1877        return self.prepend_ctes(expression, sql)

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

    1879    def schema_sql(self, expression: exp.Schema) -> str:
    1880        this = self.sql(expression, "this")
    1881        this = f"{this} " if this else ""
    1882        sql = self.schema_columns_sql(expression)
    1883        return f"{this}{sql}"

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

    1885    def schema_columns_sql(self, expression: exp.Schema) -> str:
    1886        return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"

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

    1888    def star_sql(self, expression: exp.Star) -> str:
    1889        except_ = self.expressions(expression, key="except", flat=True)
    1890        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1891        replace = self.expressions(expression, key="replace", flat=True)
    1892        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1893        return f"*{except_}{replace}"

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

    1895    def parameter_sql(self, expression: exp.Parameter) -> str:
    1896        this = self.sql(expression, "this")
    1897        return f"{self.PARAMETER_TOKEN}{this}" if self.SUPPORTS_PARAMETERS else this

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

    1899    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1900        this = self.sql(expression, "this")
    1901        kind = expression.text("kind")
    1902        if kind:
    1903            kind = f"{kind}."
    1904        return f"@@{kind}{this}"

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

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

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

    1909    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1910        alias = self.sql(expression, "alias")
    1911        alias = f"{sep}{alias}" if alias else ""
    1912
    1913        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1914        pivots = f" {pivots}" if pivots else ""
    1915
    1916        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    1917        return self.prepend_ctes(expression, sql)

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

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

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

    1923    def union_sql(self, expression: exp.Union) -> str:
    1924        return self.prepend_ctes(
    1925            expression,
    1926            self.set_operation(expression, self.union_op(expression)),
    1927        )

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

    1929    def union_op(self, expression: exp.Union) -> str:
    1930        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1931        kind = kind if expression.args.get("distinct") else " ALL"
    1932        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1933        return f"UNION{kind}{by_name}"

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

    1935    def unnest_sql(self, expression: exp.Unnest) -> str:
    1936        args = self.expressions(expression, flat=True)
    1937
    1938        alias = expression.args.get("alias")
    1939        offset = expression.args.get("offset")
    1940
    1941        if self.UNNEST_WITH_ORDINALITY:
    1942            if alias and isinstance(offset, exp.Expression):
    1943                alias = alias.copy()
    1944                alias.append("columns", offset.copy())
    1945
    1946        if alias and self.UNNEST_COLUMN_ONLY:
    1947            columns = alias.columns
    1948            alias = self.sql(columns[0]) if columns else ""
    1949        else:
    1950            alias = self.sql(alias)
    1951
    1952        alias = f" AS {alias}" if alias else alias
    1953        if self.UNNEST_WITH_ORDINALITY:
    1954            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    1955        else:
    1956            if isinstance(offset, exp.Expression):
    1957                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    1958            elif offset:
    1959                suffix = f"{alias} WITH OFFSET"
    1960            else:
    1961                suffix = alias
    1962
    1963        return f"UNNEST({args}){suffix}"

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

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

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

    1969    def window_sql(self, expression: exp.Window) -> str:
    1970        this = self.sql(expression, "this")
    1971        partition = self.partition_by_sql(expression)
    1972        order = expression.args.get("order")
    1973        order = self.order_sql(order, flat=True) if order else ""
    1974        spec = self.sql(expression, "spec")
    1975        alias = self.sql(expression, "alias")
    1976        over = self.sql(expression, "over") or "OVER"
    1977
    1978        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    1979
    1980        first = expression.args.get("first")
    1981        if first is None:
    1982            first = ""
    1983        else:
    1984            first = "FIRST" if first else "LAST"
    1985
    1986        if not partition and not order and not spec and alias:
    1987            return f"{this} {alias}"
    1988
    1989        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    1990        return f"{this} ({args})"

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

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

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

    1996    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    1997        kind = self.sql(expression, "kind")
    1998        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1999        end = (
    2000            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2001            or "CURRENT ROW"
    2002        )
    2003        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2010    def between_sql(self, expression: exp.Between) -> str:
    2011        this = self.sql(expression, "this")
    2012        low = self.sql(expression, "low")
    2013        high = self.sql(expression, "high")
    2014        return f"{this} BETWEEN {low} AND {high}"

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

    2016    def bracket_sql(self, expression: exp.Bracket) -> str:
    2017        expressions = apply_index_offset(expression.this, expression.expressions, self.INDEX_OFFSET)
    2018        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2019
    2020        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

def safebracket_sql(self, expression: sqlglot.expressions.SafeBracket) -> str: View Source

    2022    def safebracket_sql(self, expression: exp.SafeBracket) -> str:
    2023        return self.bracket_sql(expression)

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

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

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

    2028    def any_sql(self, expression: exp.Any) -> str:
    2029        this = self.sql(expression, "this")
    2030        if isinstance(expression.this, exp.Subqueryable):
    2031            this = self.wrap(this)
    2032        return f"ANY {this}"

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

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

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

    2037    def case_sql(self, expression: exp.Case) -> str:
    2038        this = self.sql(expression, "this")
    2039        statements = [f"CASE {this}" if this else "CASE"]
    2040
    2041        for e in expression.args["ifs"]:
    2042            statements.append(f"WHEN {self.sql(e, 'this')}")
    2043            statements.append(f"THEN {self.sql(e, 'true')}")
    2044
    2045        default = self.sql(expression, "default")
    2046
    2047        if default:
    2048            statements.append(f"ELSE {default}")
    2049
    2050        statements.append("END")
    2051
    2052        if self.pretty and self.text_width(statements) > self.max_text_width:
    2053            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2054
    2055        return " ".join(statements)

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

    2057    def constraint_sql(self, expression: exp.Constraint) -> str:
    2058        this = self.sql(expression, "this")
    2059        expressions = self.expressions(expression, flat=True)
    2060        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2067    def extract_sql(self, expression: exp.Extract) -> str:
    2068        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2069        expression_sql = self.sql(expression, "expression")
    2070        return f"EXTRACT({this} FROM {expression_sql})"

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

    2072    def trim_sql(self, expression: exp.Trim) -> str:
    2073        trim_type = self.sql(expression, "position")
    2074
    2075        if trim_type == "LEADING":
    2076            return self.func("LTRIM", expression.this)
    2077        elif trim_type == "TRAILING":
    2078            return self.func("RTRIM", expression.this)
    2079        else:
    2080            return self.func("TRIM", expression.this, expression.expression)

def safeconcat_sql(self, expression: sqlglot.expressions.SafeConcat) -> str: View Source

    2082    def safeconcat_sql(self, expression: exp.SafeConcat) -> str:
    2083        expressions = expression.expressions
    2084        if self.STRICT_STRING_CONCAT:
    2085            expressions = (exp.cast(e, "text") for e in expressions)
    2086        return self.func("CONCAT", *expressions)

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

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

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

    2092    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2093        expressions = self.expressions(expression, flat=True)
    2094        reference = self.sql(expression, "reference")
    2095        reference = f" {reference}" if reference else ""
    2096        delete = self.sql(expression, "delete")
    2097        delete = f" ON DELETE {delete}" if delete else ""
    2098        update = self.sql(expression, "update")
    2099        update = f" ON UPDATE {update}" if update else ""
    2100        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2102    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2103        expressions = self.expressions(expression, flat=True)
    2104        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2105        options = f" {options}" if options else ""
    2106        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

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

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

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

    2123    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    2124        null_handling = expression.args.get("null_handling")
    2125        null_handling = f" {null_handling}" if null_handling else ""
    2126        unique_keys = expression.args.get("unique_keys")
    2127        if unique_keys is not None:
    2128            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2129        else:
    2130            unique_keys = ""
    2131        return_type = self.sql(expression, "return_type")
    2132        return_type = f" RETURNING {return_type}" if return_type else ""
    2133        encoding = self.sql(expression, "encoding")
    2134        encoding = f" ENCODING {encoding}" if encoding else ""
    2135        return self.func(
    2136            "JSON_OBJECT",
    2137            *expression.expressions,
    2138            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2139        )

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

    2141    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2142        null_handling = expression.args.get("null_handling")
    2143        null_handling = f" {null_handling}" if null_handling else ""
    2144        return_type = self.sql(expression, "return_type")
    2145        return_type = f" RETURNING {return_type}" if return_type else ""
    2146        strict = " STRICT" if expression.args.get("strict") else ""
    2147        return self.func(
    2148            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2149        )

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

    2151    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2152        this = self.sql(expression, "this")
    2153        order = self.sql(expression, "order")
    2154        null_handling = expression.args.get("null_handling")
    2155        null_handling = f" {null_handling}" if null_handling else ""
    2156        return_type = self.sql(expression, "return_type")
    2157        return_type = f" RETURNING {return_type}" if return_type else ""
    2158        strict = " STRICT" if expression.args.get("strict") else ""
    2159        return self.func(
    2160            "JSON_ARRAYAGG",
    2161            this,
    2162            suffix=f"{order}{null_handling}{return_type}{strict})",
    2163        )

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

    2165    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2166        this = self.sql(expression, "this")
    2167        kind = self.sql(expression, "kind")
    2168        kind = f" {kind}" if kind else ""
    2169        path = self.sql(expression, "path")
    2170        path = f" PATH {path}" if path else ""
    2171        return f"{this}{kind}{path}"

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

    2173    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2174        this = self.sql(expression, "this")
    2175        path = self.sql(expression, "path")
    2176        path = f", {path}" if path else ""
    2177        error_handling = expression.args.get("error_handling")
    2178        error_handling = f" {error_handling}" if error_handling else ""
    2179        empty_handling = expression.args.get("empty_handling")
    2180        empty_handling = f" {empty_handling}" if empty_handling else ""
    2181        columns = f" COLUMNS ({self.expressions(expression, skip_first=True)})"
    2182        return self.func(
    2183            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling}{columns})"
    2184        )

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

    2186    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2187        this = self.sql(expression, "this")
    2188        kind = self.sql(expression, "kind")
    2189        path = self.sql(expression, "path")
    2190        path = f" {path}" if path else ""
    2191        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2192        return f"{this} {kind}{path}{as_json}"

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

    2194    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2195        this = self.sql(expression, "this")
    2196        path = self.sql(expression, "path")
    2197        path = f", {path}" if path else ""
    2198        expressions = self.expressions(expression)
    2199        with_ = (
    2200            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2201            if expressions
    2202            else ""
    2203        )
    2204        return f"OPENJSON({this}{path}){with_}"

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

    2206    def in_sql(self, expression: exp.In) -> str:
    2207        query = expression.args.get("query")
    2208        unnest = expression.args.get("unnest")
    2209        field = expression.args.get("field")
    2210        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2211
    2212        if query:
    2213            in_sql = self.wrap(query)
    2214        elif unnest:
    2215            in_sql = self.in_unnest_op(unnest)
    2216        elif field:
    2217            in_sql = self.sql(field)
    2218        else:
    2219            in_sql = f"({self.expressions(expression, flat=True)})"
    2220
    2221        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2226    def interval_sql(self, expression: exp.Interval) -> str:
    2227        unit = self.sql(expression, "unit")
    2228        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2229            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    2230        unit = f" {unit}" if unit else ""
    2231
    2232        if self.SINGLE_STRING_INTERVAL:
    2233            this = expression.this.name if expression.this else ""
    2234            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2235
    2236        this = self.sql(expression, "this")
    2237        if this:
    2238            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2239            this = f" {this}" if unwrapped else f" ({this})"
    2240
    2241        return f"INTERVAL{this}{unit}"

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

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

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

    2246    def reference_sql(self, expression: exp.Reference) -> str:
    2247        this = self.sql(expression, "this")
    2248        expressions = self.expressions(expression, flat=True)
    2249        expressions = f"({expressions})" if expressions else ""
    2250        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2251        options = f" {options}" if options else ""
    2252        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    2257    def paren_sql(self, expression: exp.Paren) -> str:
    2258        if isinstance(expression.unnest(), exp.Select):
    2259            sql = self.wrap(expression)
    2260        else:
    2261            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2262            sql = f"({sql}{self.seg(')', sep='')}"
    2263
    2264        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    2275    def alias_sql(self, expression: exp.Alias) -> str:
    2276        alias = self.sql(expression, "alias")
    2277        alias = f" AS {alias}" if alias else ""
    2278        return f"{self.sql(expression, 'this')}{alias}"

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

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

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

    2283    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2284        this = self.sql(expression, "this")
    2285        zone = self.sql(expression, "zone")
    2286        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

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

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

    2297    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2298        if not self.pretty:
    2299            return self.binary(expression, op)
    2300
    2301        sqls = tuple(
    2302            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2303            for i, e in enumerate(expression.flatten(unnest=False))
    2304        )
    2305
    2306        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2307        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2327    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2328        format_sql = self.sql(expression, "format")
    2329        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2330        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')}{format_sql})"

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

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

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

    2336    def collate_sql(self, expression: exp.Collate) -> str:
    2337        if self.COLLATE_IS_FUNC:
    2338            return self.function_fallback_sql(expression)
    2339        return self.binary(expression, "COLLATE")

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

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

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

    2344    def comment_sql(self, expression: exp.Comment) -> str:
    2345        this = self.sql(expression, "this")
    2346        kind = expression.args["kind"]
    2347        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2348        expression_sql = self.sql(expression, "expression")
    2349        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    2351    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2352        this = self.sql(expression, "this")
    2353        delete = " DELETE" if expression.args.get("delete") else ""
    2354        recompress = self.sql(expression, "recompress")
    2355        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2356        to_disk = self.sql(expression, "to_disk")
    2357        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2358        to_volume = self.sql(expression, "to_volume")
    2359        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2360        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    2362    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2363        where = self.sql(expression, "where")
    2364        group = self.sql(expression, "group")
    2365        aggregates = self.expressions(expression, key="aggregates")
    2366        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2367
    2368        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2369            return f"TTL {self.expressions(expression, flat=True)}"
    2370
    2371        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2376    def commit_sql(self, expression: exp.Commit) -> str:
    2377        chain = expression.args.get("chain")
    2378        if chain is not None:
    2379            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2380
    2381        return f"COMMIT{chain or ''}"

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

    2383    def rollback_sql(self, expression: exp.Rollback) -> str:
    2384        savepoint = expression.args.get("savepoint")
    2385        savepoint = f" TO {savepoint}" if savepoint else ""
    2386        return f"ROLLBACK{savepoint}"

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

    2388    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2389        this = self.sql(expression, "this")
    2390
    2391        dtype = self.sql(expression, "dtype")
    2392        if dtype:
    2393            collate = self.sql(expression, "collate")
    2394            collate = f" COLLATE {collate}" if collate else ""
    2395            using = self.sql(expression, "using")
    2396            using = f" USING {using}" if using else ""
    2397            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2398
    2399        default = self.sql(expression, "default")
    2400        if default:
    2401            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2402
    2403        if not expression.args.get("drop"):
    2404            self.unsupported("Unsupported ALTER COLUMN syntax")
    2405
    2406        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2408    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2409        if not self.RENAME_TABLE_WITH_DB:
    2410            # Remove db from tables
    2411            expression = expression.transform(
    2412                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2413            )
    2414        this = self.sql(expression, "this")
    2415        return f"RENAME TO {this}"

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

    2417    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2418        actions = expression.args["actions"]
    2419
    2420        if isinstance(actions[0], exp.ColumnDef):
    2421            if self.ALTER_TABLE_ADD_COLUMN_KEYWORD:
    2422                actions = self.expressions(
    2423                    expression,
    2424                    key="actions",
    2425                    prefix="ADD COLUMN ",
    2426                )
    2427            else:
    2428                actions = f"ADD {self.expressions(expression, key='actions')}"
    2429        elif isinstance(actions[0], exp.Schema):
    2430            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2431        elif isinstance(actions[0], exp.Delete):
    2432            actions = self.expressions(expression, key="actions", flat=True)
    2433        else:
    2434            actions = self.expressions(expression, key="actions", flat=True)
    2435
    2436        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2437        only = " ONLY" if expression.args.get("only") else ""
    2438        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"

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

    2440    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2441        expressions = self.expressions(expression)
    2442        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2443        return f"DROP{exists}{expressions}"

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

    2445    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2446        this = self.sql(expression, "this")
    2447        expression_ = self.sql(expression, "expression")
    2448        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2449
    2450        enforced = expression.args.get("enforced")
    2451        if enforced is not None:
    2452            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2453
    2454        return f"{add_constraint} {expression_}"

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

    2456    def distinct_sql(self, expression: exp.Distinct) -> str:
    2457        this = self.expressions(expression, flat=True)
    2458        this = f" {this}" if this else ""
    2459
    2460        on = self.sql(expression, "on")
    2461        on = f" ON {on}" if on else ""
    2462        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2470    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2471        return self.sql(
    2472            exp.Cast(
    2473                this=exp.Div(this=expression.this.copy(), expression=expression.expression.copy()),
    2474                to=exp.DataType(this=exp.DataType.Type.INT),
    2475            )
    2476        )

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

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

def safedpipe_sql(self, expression: sqlglot.expressions.SafeDPipe) -> str: View Source

    2481    def safedpipe_sql(self, expression: exp.SafeDPipe) -> str:
    2482        if self.STRICT_STRING_CONCAT:
    2483            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
    2484        return self.dpipe_sql(expression)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2519    def is_sql(self, expression: exp.Is) -> str:
    2520        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    2521            return self.sql(
    2522                expression.this if expression.expression.this else exp.not_(expression.this)
    2523            )
    2524        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2568    def log_sql(self, expression: exp.Log) -> str:
    2569        args = list(expression.args.values())
    2570        if not self.LOG_BASE_FIRST:
    2571            args.reverse()
    2572        return self.func("LOG", *args)

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

    2574    def use_sql(self, expression: exp.Use) -> str:
    2575        kind = self.sql(expression, "kind")
    2576        kind = f" {kind}" if kind else ""
    2577        this = self.sql(expression, "this")
    2578        this = f" {this}" if this else ""
    2579        return f"USE{kind}{this}"

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

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

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

    2585    def function_fallback_sql(self, expression: exp.Func) -> str:
    2586        args = []
    2587
    2588        for key in expression.arg_types:
    2589            arg_value = expression.args.get(key)
    2590
    2591            if isinstance(arg_value, list):
    2592                for value in arg_value:
    2593                    args.append(value)
    2594            elif arg_value is not None:
    2595                args.append(arg_value)
    2596
    2597        if self.normalize_functions:
    2598            name = expression.sql_name()
    2599        else:
    2600            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    2601
    2602        return self.func(name, *args)

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

    2604    def func(
    2605        self,
    2606        name: str,
    2607        *args: t.Optional[exp.Expression | str],
    2608        prefix: str = "(",
    2609        suffix: str = ")",
    2610    ) -> str:
    2611        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

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

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

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

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

    2622    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2623        return format_time(
    2624            self.sql(expression, "format"), self.INVERSE_TIME_MAPPING, self.INVERSE_TIME_TRIE
    2625        )

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

    2627    def expressions(
    2628        self,
    2629        expression: t.Optional[exp.Expression] = None,
    2630        key: t.Optional[str] = None,
    2631        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    2632        flat: bool = False,
    2633        indent: bool = True,
    2634        skip_first: bool = False,
    2635        sep: str = ", ",
    2636        prefix: str = "",
    2637    ) -> str:
    2638        expressions = expression.args.get(key or "expressions") if expression else sqls
    2639
    2640        if not expressions:
    2641            return ""
    2642
    2643        if flat:
    2644            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    2645
    2646        num_sqls = len(expressions)
    2647
    2648        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2649        pad = " " * self.pad
    2650        stripped_sep = sep.strip()
    2651
    2652        result_sqls = []
    2653        for i, e in enumerate(expressions):
    2654            sql = self.sql(e, comment=False)
    2655            if not sql:
    2656                continue
    2657
    2658            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2659
    2660            if self.pretty:
    2661                if self.leading_comma:
    2662                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2663                else:
    2664                    result_sqls.append(
    2665                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2666                    )
    2667            else:
    2668                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2669
    2670        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2671        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

    2673    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2674        flat = flat or isinstance(expression.parent, exp.Properties)
    2675        expressions_sql = self.expressions(expression, flat=flat)
    2676        if flat:
    2677            return f"{op} {expressions_sql}"
    2678        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    2680    def naked_property(self, expression: exp.Property) -> str:
    2681        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2682        if not property_name:
    2683            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2684        return f"{property_name} {self.sql(expression, 'this')}"

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

    2686    def set_operation(self, expression: exp.Expression, op: str) -> str:
    2687        this = self.sql(expression, "this")
    2688        op = self.seg(op)
    2689        return self.query_modifiers(
    2690            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2691        )

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

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

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

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

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

    2699    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2700        this = self.sql(expression, "this")
    2701        expressions = self.no_identify(self.expressions, expression)
    2702        expressions = (
    2703            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2704        )
    2705        return f"{this}{expressions}"

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

    2707    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2708        this = self.sql(expression, "this")
    2709        expressions = self.expressions(expression, flat=True)
    2710        return f"{this}({expressions})"

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

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

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

    2715    def when_sql(self, expression: exp.When) -> str:
    2716        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2717        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2718        condition = self.sql(expression, "condition")
    2719        condition = f" AND {condition}" if condition else ""
    2720
    2721        then_expression = expression.args.get("then")
    2722        if isinstance(then_expression, exp.Insert):
    2723            then = f"INSERT {self.sql(then_expression, 'this')}"
    2724            if "expression" in then_expression.args:
    2725                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2726        elif isinstance(then_expression, exp.Update):
    2727            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2728                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2729            else:
    2730                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2731        else:
    2732            then = self.sql(then_expression)
    2733        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    2735    def merge_sql(self, expression: exp.Merge) -> str:
    2736        table = expression.this
    2737        table_alias = ""
    2738
    2739        hints = table.args.get("hints")
    2740        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    2741            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    2742            table = table.copy()
    2743            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    2744
    2745        this = self.sql(table)
    2746        using = f"USING {self.sql(expression, 'using')}"
    2747        on = f"ON {self.sql(expression, 'on')}"
    2748        expressions = self.expressions(expression, sep=" ")
    2749
    2750        return f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"

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

    2752    def tochar_sql(self, expression: exp.ToChar) -> str:
    2753        if expression.args.get("format"):
    2754            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2755
    2756        return self.sql(exp.cast(expression.this, "text"))

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

    2758    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2759        this = self.sql(expression, "this")
    2760        kind = self.sql(expression, "kind")
    2761        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2762        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2763        return f"{this}({kind}{args})"

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

    2765    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2766        this = self.sql(expression, "this")
    2767        max = self.sql(expression, "max")
    2768        min = self.sql(expression, "min")
    2769        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    2777    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    2778        expressions = self.expressions(expression, key="expressions", flat=True)
    2779        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    2780        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    2781        buckets = self.sql(expression, "buckets")
    2782        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    2784    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    2785        this = self.sql(expression, "this")
    2786        having = self.sql(expression, "having")
    2787
    2788        if having:
    2789            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    2790
    2791        return self.func("ANY_VALUE", this)

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

    2793    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    2794        transform = self.func("TRANSFORM", *expression.expressions)
    2795        row_format_before = self.sql(expression, "row_format_before")
    2796        row_format_before = f" {row_format_before}" if row_format_before else ""
    2797        record_writer = self.sql(expression, "record_writer")
    2798        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    2799        using = f" USING {self.sql(expression, 'command_script')}"
    2800        schema = self.sql(expression, "schema")
    2801        schema = f" AS {schema}" if schema else ""
    2802        row_format_after = self.sql(expression, "row_format_after")
    2803        row_format_after = f" {row_format_after}" if row_format_after else ""
    2804        record_reader = self.sql(expression, "record_reader")
    2805        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    2806        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

    2808    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    2809        key_block_size = self.sql(expression, "key_block_size")
    2810        if key_block_size:
    2811            return f"KEY_BLOCK_SIZE = {key_block_size}"
    2812
    2813        using = self.sql(expression, "using")
    2814        if using:
    2815            return f"USING {using}"
    2816
    2817        parser = self.sql(expression, "parser")
    2818        if parser:
    2819            return f"WITH PARSER {parser}"
    2820
    2821        comment = self.sql(expression, "comment")
    2822        if comment:
    2823            return f"COMMENT {comment}"
    2824
    2825        visible = expression.args.get("visible")
    2826        if visible is not None:
    2827            return "VISIBLE" if visible else "INVISIBLE"
    2828
    2829        engine_attr = self.sql(expression, "engine_attr")
    2830        if engine_attr:
    2831            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    2832
    2833        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    2834        if secondary_engine_attr:
    2835            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    2836
    2837        self.unsupported("Unsupported index constraint option.")
    2838        return ""

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

    2840    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    2841        kind = self.sql(expression, "kind")
    2842        kind = f"{kind} INDEX" if kind else "INDEX"
    2843        this = self.sql(expression, "this")
    2844        this = f" {this}" if this else ""
    2845        index_type = self.sql(expression, "index_type")
    2846        index_type = f" USING {index_type}" if index_type else ""
    2847        schema = self.sql(expression, "schema")
    2848        schema = f" {schema}" if schema else ""
    2849        options = self.expressions(expression, key="options", sep=" ")
    2850        options = f" {options}" if options else ""
    2851        return f"{kind}{this}{index_type}{schema}{options}"

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

    2853    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    2854        if self.NVL2_SUPPORTED:
    2855            return self.function_fallback_sql(expression)
    2856
    2857        case = exp.Case().when(
    2858            expression.this.is_(exp.null()).not_(copy=False),
    2859            expression.args["true"].copy(),
    2860            copy=False,
    2861        )
    2862        else_cond = expression.args.get("false")
    2863        if else_cond:
    2864            case.else_(else_cond.copy(), copy=False)
    2865
    2866        return self.sql(case)

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

    2868    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    2869        this = self.sql(expression, "this")
    2870        expr = self.sql(expression, "expression")
    2871        iterator = self.sql(expression, "iterator")
    2872        condition = self.sql(expression, "condition")
    2873        condition = f" IF {condition}" if condition else ""
    2874        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

def cached_generator( cache: Optional[Dict[int, str]] = None) -> Callable[[sqlglot.expressions.Expression], str]: View Source

    2883def cached_generator(
    2884    cache: t.Optional[t.Dict[int, str]] = None
    2885) -> t.Callable[[exp.Expression], str]:
    2886    """Returns a cached generator."""
    2887    cache = {} if cache is None else cache
    2888    generator = Generator(normalize=True, identify="safe")
    2889    return lambda e: generator.generate(e, cache)

Returns a cached generator.