sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

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

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

    402    def __init__(
    403        self,
    404        pretty: t.Optional[bool] = None,
    405        identify: str | bool = False,
    406        normalize: bool = False,
    407        pad: int = 2,
    408        indent: int = 2,
    409        normalize_functions: t.Optional[str | bool] = None,
    410        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    411        max_unsupported: int = 3,
    412        leading_comma: bool = False,
    413        max_text_width: int = 80,
    414        comments: bool = True,
    415    ):
    416        import sqlglot
    417
    418        self.pretty = pretty if pretty is not None else sqlglot.pretty
    419        self.identify = identify
    420        self.normalize = normalize
    421        self.pad = pad
    422        self._indent = indent
    423        self.unsupported_level = unsupported_level
    424        self.max_unsupported = max_unsupported
    425        self.leading_comma = leading_comma
    426        self.max_text_width = max_text_width
    427        self.comments = comments
    428
    429        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    430        self.normalize_functions = (
    431            self.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    432        )
    433
    434        self.unsupported_messages: t.List[str] = []
    435        self._escaped_quote_end: str = self.TOKENIZER_CLASS.STRING_ESCAPES[0] + self.QUOTE_END
    436        self._escaped_identifier_end: str = (
    437            self.TOKENIZER_CLASS.IDENTIFIER_ESCAPES[0] + self.IDENTIFIER_END
    438        )
    439        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.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
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.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>}
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

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

    477    def unsupported(self, message: str) -> None:
    478        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    479            raise UnsupportedError(message)
    480        self.unsupported_messages.append(message)

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

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

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

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

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

    488    def pad_comment(self, comment: str) -> str:
    489        comment = " " + comment if comment[0].strip() else comment
    490        comment = comment + " " if comment[-1].strip() else comment
    491        return comment

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

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

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

    524    def wrap(self, expression: exp.Expression | str) -> str:
    525        this_sql = self.indent(
    526            self.sql(expression)
    527            if isinstance(expression, (exp.Select, exp.Union))
    528            else self.sql(expression, "this"),
    529            level=1,
    530            pad=0,
    531        )
    532        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    534    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    535        original = self.identify
    536        self.identify = False
    537        result = func(*args, **kwargs)
    538        self.identify = original
    539        return result

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

    541    def normalize_func(self, name: str) -> str:
    542        if self.normalize_functions == "upper" or self.normalize_functions is True:
    543            return name.upper()
    544        if self.normalize_functions == "lower":
    545            return name.lower()
    546        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    743    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    744        desc = expression.args.get("desc")
    745        if desc is not None:
    746            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    747        return f"PRIMARY KEY"

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

    749    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    750        this = self.sql(expression, "this")
    751        this = f" {this}" if this else ""
    752        index_type = expression.args.get("index_type")
    753        index_type = f" USING {index_type}" if index_type else ""
    754        return f"UNIQUE{this}{index_type}"

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

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

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

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

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

    846    def clone_sql(self, expression: exp.Clone) -> str:
    847        this = self.sql(expression, "this")
    848        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    849        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    850        this = f"{shallow}{keyword} {this}"
    851        when = self.sql(expression, "when")
    852
    853        if when:
    854            kind = self.sql(expression, "kind")
    855            expr = self.sql(expression, "expression")
    856            return f"{this} {when} ({kind} => {expr})"
    857
    858        return this

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

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

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

    863    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    864        with_ = self.sql(expression, "with")
    865        if with_:
    866            sql = f"{with_}{self.sep()}{sql}"
    867        return sql

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

    869    def with_sql(self, expression: exp.With) -> str:
    870        sql = self.expressions(expression, flat=True)
    871        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
    872
    873        return f"WITH {recursive}{sql}"

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

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

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

    879    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    880        alias = self.sql(expression, "this")
    881        columns = self.expressions(expression, key="columns", flat=True)
    882        columns = f"({columns})" if columns else ""
    883        return f"{alias}{columns}"

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

    885    def bitstring_sql(self, expression: exp.BitString) -> str:
    886        this = self.sql(expression, "this")
    887        if self.BIT_START:
    888            return f"{self.BIT_START}{this}{self.BIT_END}"
    889        return f"{int(this, 2)}"

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

    891    def hexstring_sql(self, expression: exp.HexString) -> str:
    892        this = self.sql(expression, "this")
    893        if self.HEX_START:
    894            return f"{self.HEX_START}{this}{self.HEX_END}"
    895        return f"{int(this, 16)}"

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

    897    def bytestring_sql(self, expression: exp.ByteString) -> str:
    898        this = self.sql(expression, "this")
    899        if self.BYTE_START:
    900            return f"{self.BYTE_START}{this}{self.BYTE_END}"
    901        return this

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

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

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

    907    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    908        this = self.sql(expression, "this")
    909        specifier = self.sql(expression, "expression")
    910        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    911        return f"{this}{specifier}"

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

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

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

    950    def directory_sql(self, expression: exp.Directory) -> str:
    951        local = "LOCAL " if expression.args.get("local") else ""
    952        row_format = self.sql(expression, "row_format")
    953        row_format = f" {row_format}" if row_format else ""
    954        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    956    def delete_sql(self, expression: exp.Delete) -> str:
    957        this = self.sql(expression, "this")
    958        this = f" FROM {this}" if this else ""
    959        using = self.sql(expression, "using")
    960        using = f" USING {using}" if using else ""
    961        where = self.sql(expression, "where")
    962        returning = self.sql(expression, "returning")
    963        limit = self.sql(expression, "limit")
    964        tables = self.expressions(expression, key="tables")
    965        tables = f" {tables}" if tables else ""
    966        if self.RETURNING_END:
    967            expression_sql = f"{this}{using}{where}{returning}{limit}"
    968        else:
    969            expression_sql = f"{returning}{this}{using}{where}{limit}"
    970        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    972    def drop_sql(self, expression: exp.Drop) -> str:
    973        this = self.sql(expression, "this")
    974        kind = expression.args["kind"]
    975        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    976        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    977        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    978        cascade = " CASCADE" if expression.args.get("cascade") else ""
    979        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    980        purge = " PURGE" if expression.args.get("purge") else ""
    981        return (
    982            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
    983        )

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

    985    def except_sql(self, expression: exp.Except) -> str:
    986        return self.prepend_ctes(
    987            expression,
    988            self.set_operation(expression, self.except_op(expression)),
    989        )

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

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

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

     994    def fetch_sql(self, expression: exp.Fetch) -> str:
     995        direction = expression.args.get("direction")
     996        direction = f" {direction.upper()}" if direction else ""
     997        count = expression.args.get("count")
     998        count = f" {count}" if count else ""
     999        if expression.args.get("percent"):
    1000            count = f"{count} PERCENT"
    1001        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1002        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1004    def filter_sql(self, expression: exp.Filter) -> str:
    1005        if self.AGGREGATE_FILTER_SUPPORTED:
    1006            this = self.sql(expression, "this")
    1007            where = self.sql(expression, "expression").strip()
    1008            return f"{this} FILTER({where})"
    1009
    1010        agg = expression.this.copy()
    1011        agg_arg = agg.this
    1012        cond = expression.expression.this
    1013        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1014        return self.sql(agg)

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

    1016    def hint_sql(self, expression: exp.Hint) -> str:
    1017        if not self.QUERY_HINTS:
    1018            self.unsupported("Hints are not supported")
    1019            return ""
    1020
    1021        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1023    def index_sql(self, expression: exp.Index) -> str:
    1024        unique = "UNIQUE " if expression.args.get("unique") else ""
    1025        primary = "PRIMARY " if expression.args.get("primary") else ""
    1026        amp = "AMP " if expression.args.get("amp") else ""
    1027        name = self.sql(expression, "this")
    1028        name = f"{name} " if name else ""
    1029        table = self.sql(expression, "table")
    1030        table = f"{self.INDEX_ON} {table}" if table else ""
    1031        using = self.sql(expression, "using")
    1032        using = f" USING {using}" if using else ""
    1033        index = "INDEX " if not table else ""
    1034        columns = self.expressions(expression, key="columns", flat=True)
    1035        columns = f"({columns})" if columns else ""
    1036        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1037        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1038        where = self.sql(expression, "where")
    1039        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"

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

    1041    def identifier_sql(self, expression: exp.Identifier) -> str:
    1042        text = expression.name
    1043        lower = text.lower()
    1044        text = lower if self.normalize and not expression.quoted else text
    1045        text = text.replace(self.IDENTIFIER_END, self._escaped_identifier_end)
    1046        if (
    1047            expression.quoted
    1048            or self.can_identify(text, self.identify)
    1049            or lower in self.RESERVED_KEYWORDS
    1050            or (not self.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1051        ):
    1052            text = f"{self.IDENTIFIER_START}{text}{self.IDENTIFIER_END}"
    1053        return text

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

    1055    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1056        input_format = self.sql(expression, "input_format")
    1057        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1058        output_format = self.sql(expression, "output_format")
    1059        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1060        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1069    def properties_sql(self, expression: exp.Properties) -> str:
    1070        root_properties = []
    1071        with_properties = []
    1072
    1073        for p in expression.expressions:
    1074            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1075            if p_loc == exp.Properties.Location.POST_WITH:
    1076                with_properties.append(p.copy())
    1077            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1078                root_properties.append(p.copy())
    1079
    1080        return self.root_properties(
    1081            exp.Properties(expressions=root_properties)
    1082        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    1084    def root_properties(self, properties: exp.Properties) -> str:
    1085        if properties.expressions:
    1086            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1087        return ""

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

    1089    def properties(
    1090        self,
    1091        properties: exp.Properties,
    1092        prefix: str = "",
    1093        sep: str = ", ",
    1094        suffix: str = "",
    1095        wrapped: bool = True,
    1096    ) -> str:
    1097        if properties.expressions:
    1098            expressions = self.expressions(properties, sep=sep, indent=False)
    1099            if expressions:
    1100                expressions = self.wrap(expressions) if wrapped else expressions
    1101                return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
    1102        return ""

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

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

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

    1107    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1108        properties_locs = defaultdict(list)
    1109        for p in properties.expressions:
    1110            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1111            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1112                properties_locs[p_loc].append(p.copy())
    1113            else:
    1114                self.unsupported(f"Unsupported property {p.key}")
    1115
    1116        return properties_locs

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

    1118    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1119        if isinstance(expression.this, exp.Dot):
    1120            return self.sql(expression, "this")
    1121        return f"'{expression.name}'" if string_key else expression.name

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

    1123    def property_sql(self, expression: exp.Property) -> str:
    1124        property_cls = expression.__class__
    1125        if property_cls == exp.Property:
    1126            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1127
    1128        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1129        if not property_name:
    1130            self.unsupported(f"Unsupported property {expression.key}")
    1131
    1132        return f"{property_name}={self.sql(expression, 'this')}"

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

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

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

    1139    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1140        no = "NO " if expression.args.get("no") else ""
    1141        protection = " PROTECTION" if expression.args.get("protection") else ""
    1142        return f"{no}FALLBACK{protection}"

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

    1144    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1145        no = "NO " if expression.args.get("no") else ""
    1146        local = expression.args.get("local")
    1147        local = f"{local} " if local else ""
    1148        dual = "DUAL " if expression.args.get("dual") else ""
    1149        before = "BEFORE " if expression.args.get("before") else ""
    1150        after = "AFTER " if expression.args.get("after") else ""
    1151        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1153    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1154        freespace = self.sql(expression, "this")
    1155        percent = " PERCENT" if expression.args.get("percent") else ""
    1156        return f"FREESPACE={freespace}{percent}"

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

    1158    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1159        if expression.args.get("default"):
    1160            property = "DEFAULT"
    1161        elif expression.args.get("on"):
    1162            property = "ON"
    1163        else:
    1164            property = "OFF"
    1165        return f"CHECKSUM={property}"

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

    1167    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1168        if expression.args.get("no"):
    1169            return "NO MERGEBLOCKRATIO"
    1170        if expression.args.get("default"):
    1171            return "DEFAULT MERGEBLOCKRATIO"
    1172
    1173        percent = " PERCENT" if expression.args.get("percent") else ""
    1174        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1176    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1177        default = expression.args.get("default")
    1178        minimum = expression.args.get("minimum")
    1179        maximum = expression.args.get("maximum")
    1180        if default or minimum or maximum:
    1181            if default:
    1182                prop = "DEFAULT"
    1183            elif minimum:
    1184                prop = "MINIMUM"
    1185            else:
    1186                prop = "MAXIMUM"
    1187            return f"{prop} DATABLOCKSIZE"
    1188        units = expression.args.get("units")
    1189        units = f" {units}" if units else ""
    1190        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1192    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1193        autotemp = expression.args.get("autotemp")
    1194        always = expression.args.get("always")
    1195        default = expression.args.get("default")
    1196        manual = expression.args.get("manual")
    1197        never = expression.args.get("never")
    1198
    1199        if autotemp is not None:
    1200            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1201        elif always:
    1202            prop = "ALWAYS"
    1203        elif default:
    1204            prop = "DEFAULT"
    1205        elif manual:
    1206            prop = "MANUAL"
    1207        elif never:
    1208            prop = "NEVER"
    1209        return f"BLOCKCOMPRESSION={prop}"

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

    1211    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1212        no = expression.args.get("no")
    1213        no = " NO" if no else ""
    1214        concurrent = expression.args.get("concurrent")
    1215        concurrent = " CONCURRENT" if concurrent else ""
    1216
    1217        for_ = ""
    1218        if expression.args.get("for_all"):
    1219            for_ = " FOR ALL"
    1220        elif expression.args.get("for_insert"):
    1221            for_ = " FOR INSERT"
    1222        elif expression.args.get("for_none"):
    1223            for_ = " FOR NONE"
    1224        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

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

    1226    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1227        kind = expression.args.get("kind")
    1228        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1229        for_or_in = expression.args.get("for_or_in")
    1230        lock_type = expression.args.get("lock_type")
    1231        override = " OVERRIDE" if expression.args.get("override") else ""
    1232        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"

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

    1234    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1235        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1236        statistics = expression.args.get("statistics")
    1237        statistics_sql = ""
    1238        if statistics is not None:
    1239            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1240        return f"{data_sql}{statistics_sql}"

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

    1242    def insert_sql(self, expression: exp.Insert) -> str:
    1243        overwrite = expression.args.get("overwrite")
    1244
    1245        if isinstance(expression.this, exp.Directory):
    1246            this = " OVERWRITE" if overwrite else " INTO"
    1247        else:
    1248            this = " OVERWRITE TABLE" if overwrite else " INTO"
    1249
    1250        alternative = expression.args.get("alternative")
    1251        alternative = f" OR {alternative}" if alternative else ""
    1252        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1253
    1254        this = f"{this} {self.sql(expression, 'this')}"
    1255
    1256        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1257        partition_sql = (
    1258            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1259        )
    1260        where = self.sql(expression, "where")
    1261        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1262        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1263        conflict = self.sql(expression, "conflict")
    1264        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1265        returning = self.sql(expression, "returning")
    1266
    1267        if self.RETURNING_END:
    1268            expression_sql = f"{expression_sql}{conflict}{returning}"
    1269        else:
    1270            expression_sql = f"{returning}{expression_sql}{conflict}"
    1271
    1272        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1273        return self.prepend_ctes(expression, sql)

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

    1275    def intersect_sql(self, expression: exp.Intersect) -> str:
    1276        return self.prepend_ctes(
    1277            expression,
    1278            self.set_operation(expression, self.intersect_op(expression)),
    1279        )

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

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

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

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

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

    1287    def kill_sql(self, expression: exp.Kill) -> str:
    1288        kind = self.sql(expression, "kind")
    1289        kind = f" {kind}" if kind else ""
    1290        this = self.sql(expression, "this")
    1291        this = f" {this}" if this else ""
    1292        return f"KILL{kind}{this}"

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

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

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

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

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

    1300    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1301        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1302        constraint = self.sql(expression, "constraint")
    1303        if constraint:
    1304            constraint = f"ON CONSTRAINT {constraint}"
    1305        key = self.expressions(expression, key="key", flat=True)
    1306        do = "" if expression.args.get("duplicate") else " DO "
    1307        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1308        expressions = self.expressions(expression, flat=True)
    1309        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1310        if expressions:
    1311            expressions = f"UPDATE {set_keyword}{expressions}"
    1312        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"

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

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

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

    1317    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1318        fields = expression.args.get("fields")
    1319        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1320        escaped = expression.args.get("escaped")
    1321        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1322        items = expression.args.get("collection_items")
    1323        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1324        keys = expression.args.get("map_keys")
    1325        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1326        lines = expression.args.get("lines")
    1327        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1328        null = expression.args.get("null")
    1329        null = f" NULL DEFINED AS {null}" if null else ""
    1330        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1335    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1336        this = f"{self.sql(expression, 'this')} INDEX"
    1337        target = self.sql(expression, "target")
    1338        target = f" FOR {target}" if target else ""
    1339        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1341    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1342        table = ".".join(
    1343            part
    1344            for part in [
    1345                self.sql(expression, "catalog"),
    1346                self.sql(expression, "db"),
    1347                self.sql(expression, "this"),
    1348            ]
    1349            if part
    1350        )
    1351
    1352        version = self.sql(expression, "version")
    1353        version = f" {version}" if version else ""
    1354        alias = self.sql(expression, "alias")
    1355        alias = f"{sep}{alias}" if alias else ""
    1356        hints = self.expressions(expression, key="hints", sep=" ")
    1357        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1358        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1359        pivots = f" {pivots}" if pivots else ""
    1360        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
    1361        laterals = self.expressions(expression, key="laterals", sep="")
    1362
    1363        file_format = self.sql(expression, "format")
    1364        if file_format:
    1365            pattern = self.sql(expression, "pattern")
    1366            pattern = f", PATTERN => {pattern}" if pattern else ""
    1367            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1368
    1369        index = self.sql(expression, "index")
    1370        index = f" AT {index}" if index else ""
    1371
    1372        return f"{table}{version}{file_format}{alias}{index}{hints}{pivots}{joins}{laterals}"

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

    1374    def tablesample_sql(
    1375        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1376    ) -> str:
    1377        if self.ALIAS_POST_TABLESAMPLE and expression.this.alias:
    1378            table = expression.this.copy()
    1379            table.set("alias", None)
    1380            this = self.sql(table)
    1381            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1382        else:
    1383            this = self.sql(expression, "this")
    1384            alias = ""
    1385
    1386        method = self.sql(expression, "method")
    1387        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1388        numerator = self.sql(expression, "bucket_numerator")
    1389        denominator = self.sql(expression, "bucket_denominator")
    1390        field = self.sql(expression, "bucket_field")
    1391        field = f" ON {field}" if field else ""
    1392        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1393        percent = self.sql(expression, "percent")
    1394        percent = f"{percent} PERCENT" if percent else ""
    1395        rows = self.sql(expression, "rows")
    1396        rows = f"{rows} ROWS" if rows else ""
    1397
    1398        size = self.sql(expression, "size")
    1399        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1400            size = f"{size} PERCENT"
    1401
    1402        seed = self.sql(expression, "seed")
    1403        seed = f" {seed_prefix} ({seed})" if seed else ""
    1404        kind = expression.args.get("kind", "TABLESAMPLE")
    1405
    1406        expr = f"{bucket}{percent}{rows}{size}"
    1407        if self.TABLESAMPLE_REQUIRES_PARENS:
    1408            expr = f"({expr})"
    1409
    1410        return f"{this} {kind} {method}{expr}{seed}{alias}"

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

    1412    def pivot_sql(self, expression: exp.Pivot) -> str:
    1413        expressions = self.expressions(expression, flat=True)
    1414
    1415        if expression.this:
    1416            this = self.sql(expression, "this")
    1417            if not expressions:
    1418                return f"UNPIVOT {this}"
    1419
    1420            on = f"{self.seg('ON')} {expressions}"
    1421            using = self.expressions(expression, key="using", flat=True)
    1422            using = f"{self.seg('USING')} {using}" if using else ""
    1423            group = self.sql(expression, "group")
    1424            return f"PIVOT {this}{on}{using}{group}"
    1425
    1426        alias = self.sql(expression, "alias")
    1427        alias = f" AS {alias}" if alias else ""
    1428        unpivot = expression.args.get("unpivot")
    1429        direction = "UNPIVOT" if unpivot else "PIVOT"
    1430        field = self.sql(expression, "field")
    1431        include_nulls = expression.args.get("include_nulls")
    1432        if include_nulls is not None:
    1433            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1434        else:
    1435            nulls = ""
    1436        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1438    def version_sql(self, expression: exp.Version) -> str:
    1439        this = f"FOR {expression.name}"
    1440        kind = expression.text("kind")
    1441        expr = self.sql(expression, "expression")
    1442        return f"{this} {kind} {expr}"

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

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

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

    1447    def update_sql(self, expression: exp.Update) -> str:
    1448        this = self.sql(expression, "this")
    1449        set_sql = self.expressions(expression, flat=True)
    1450        from_sql = self.sql(expression, "from")
    1451        where_sql = self.sql(expression, "where")
    1452        returning = self.sql(expression, "returning")
    1453        order = self.sql(expression, "order")
    1454        limit = self.sql(expression, "limit")
    1455        if self.RETURNING_END:
    1456            expression_sql = f"{from_sql}{where_sql}{returning}"
    1457        else:
    1458            expression_sql = f"{returning}{from_sql}{where_sql}"
    1459        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1460        return self.prepend_ctes(expression, sql)

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

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

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

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

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

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

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

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

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

    1516    def group_sql(self, expression: exp.Group) -> str:
    1517        group_by = self.op_expressions("GROUP BY", expression)
    1518
    1519        if expression.args.get("all"):
    1520            return f"{group_by} ALL"
    1521
    1522        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1523        grouping_sets = (
    1524            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1525        )
    1526
    1527        cube = expression.args.get("cube", [])
    1528        if seq_get(cube, 0) is True:
    1529            return f"{group_by}{self.seg('WITH CUBE')}"
    1530        else:
    1531            cube_sql = self.expressions(expression, key="cube", indent=False)
    1532            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1533
    1534        rollup = expression.args.get("rollup", [])
    1535        if seq_get(rollup, 0) is True:
    1536            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1537        else:
    1538            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1539            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1540
    1541        groupings = csv(
    1542            grouping_sets,
    1543            cube_sql,
    1544            rollup_sql,
    1545            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1546            sep=self.GROUPINGS_SEP,
    1547        )
    1548
    1549        if expression.args.get("expressions") and groupings:
    1550            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1551
    1552        return f"{group_by}{groupings}"

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

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

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

    1558    def connect_sql(self, expression: exp.Connect) -> str:
    1559        start = self.sql(expression, "start")
    1560        start = self.seg(f"START WITH {start}") if start else ""
    1561        connect = self.sql(expression, "connect")
    1562        connect = self.seg(f"CONNECT BY {connect}")
    1563        return start + connect

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

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

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

    1568    def join_sql(self, expression: exp.Join) -> str:
    1569        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1570            side = None
    1571        else:
    1572            side = expression.side
    1573
    1574        op_sql = " ".join(
    1575            op
    1576            for op in (
    1577                expression.method,
    1578                "GLOBAL" if expression.args.get("global") else None,
    1579                side,
    1580                expression.kind,
    1581                expression.hint if self.JOIN_HINTS else None,
    1582            )
    1583            if op
    1584        )
    1585        on_sql = self.sql(expression, "on")
    1586        using = expression.args.get("using")
    1587
    1588        if not on_sql and using:
    1589            on_sql = csv(*(self.sql(column) for column in using))
    1590
    1591        this_sql = self.sql(expression, "this")
    1592
    1593        if on_sql:
    1594            on_sql = self.indent(on_sql, skip_first=True)
    1595            space = self.seg(" " * self.pad) if self.pretty else " "
    1596            if using:
    1597                on_sql = f"{space}USING ({on_sql})"
    1598            else:
    1599                on_sql = f"{space}ON {on_sql}"
    1600        elif not op_sql:
    1601            return f", {this_sql}"
    1602
    1603        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1604        return f"{self.seg(op_sql)} {this_sql}{on_sql}"

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

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

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

    1611    def lateral_sql(self, expression: exp.Lateral) -> str:
    1612        this = self.sql(expression, "this")
    1613
    1614        if isinstance(expression.this, exp.Subquery):
    1615            return f"LATERAL {this}"
    1616
    1617        if expression.args.get("view"):
    1618            alias = expression.args["alias"]
    1619            columns = self.expressions(alias, key="columns", flat=True)
    1620            table = f" {alias.name}" if alias.name else ""
    1621            columns = f" AS {columns}" if columns else ""
    1622            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1623            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1624
    1625        alias = self.sql(expression, "alias")
    1626        alias = f" AS {alias}" if alias else ""
    1627        return f"LATERAL {this}{alias}"

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

    1629    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1630        this = self.sql(expression, "this")
    1631        args = ", ".join(
    1632            sql
    1633            for sql in (
    1634                self.sql(expression, "offset"),
    1635                self.sql(expression, "expression"),
    1636            )
    1637            if sql
    1638        )
    1639        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args}"

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

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

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

    1645    def setitem_sql(self, expression: exp.SetItem) -> str:
    1646        kind = self.sql(expression, "kind")
    1647        kind = f"{kind} " if kind else ""
    1648        this = self.sql(expression, "this")
    1649        expressions = self.expressions(expression)
    1650        collate = self.sql(expression, "collate")
    1651        collate = f" COLLATE {collate}" if collate else ""
    1652        global_ = "GLOBAL " if expression.args.get("global") else ""
    1653        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1655    def set_sql(self, expression: exp.Set) -> str:
    1656        expressions = (
    1657            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1658        )
    1659        tag = " TAG" if expression.args.get("tag") else ""
    1660        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    1665    def lock_sql(self, expression: exp.Lock) -> str:
    1666        if not self.LOCKING_READS_SUPPORTED:
    1667            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1668            return ""
    1669
    1670        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1671        expressions = self.expressions(expression, flat=True)
    1672        expressions = f" OF {expressions}" if expressions else ""
    1673        wait = expression.args.get("wait")
    1674
    1675        if wait is not None:
    1676            if isinstance(wait, exp.Literal):
    1677                wait = f" WAIT {self.sql(wait)}"
    1678            else:
    1679                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1680
    1681        return f"{lock_type}{expressions}{wait or ''}"

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

    1683    def literal_sql(self, expression: exp.Literal) -> str:
    1684        text = expression.this or ""
    1685        if expression.is_string:
    1686            text = f"{self.QUOTE_START}{self.escape_str(text)}{self.QUOTE_END}"
    1687        return text

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

    1689    def escape_str(self, text: str) -> str:
    1690        text = text.replace(self.QUOTE_END, self._escaped_quote_end)
    1691        if self.INVERSE_ESCAPE_SEQUENCES:
    1692            text = "".join(self.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1693        elif self.pretty:
    1694            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1695        return text

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

    1697    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1698        local = " LOCAL" if expression.args.get("local") else ""
    1699        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1700        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1701        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1702        partition = self.sql(expression, "partition")
    1703        partition = f" {partition}" if partition else ""
    1704        input_format = self.sql(expression, "input_format")
    1705        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1706        serde = self.sql(expression, "serde")
    1707        serde = f" SERDE {serde}" if serde else ""
    1708        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1710    def null_sql(self, *_) -> str:
    1711        return "NULL"

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

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

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

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

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

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

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

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

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

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

    1730    def ordered_sql(self, expression: exp.Ordered) -> str:
    1731        desc = expression.args.get("desc")
    1732        asc = not desc
    1733
    1734        nulls_first = expression.args.get("nulls_first")
    1735        nulls_last = not nulls_first
    1736        nulls_are_large = self.NULL_ORDERING == "nulls_are_large"
    1737        nulls_are_small = self.NULL_ORDERING == "nulls_are_small"
    1738        nulls_are_last = self.NULL_ORDERING == "nulls_are_last"
    1739
    1740        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    1741        nulls_sort_change = ""
    1742        if nulls_first and (
    1743            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1744        ):
    1745            nulls_sort_change = " NULLS FIRST"
    1746        elif (
    1747            nulls_last
    1748            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1749            and not nulls_are_last
    1750        ):
    1751            nulls_sort_change = " NULLS LAST"
    1752
    1753        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1754            self.unsupported(
    1755                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1756            )
    1757            nulls_sort_change = ""
    1758
    1759        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

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

    1761    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1762        partition = self.partition_by_sql(expression)
    1763        order = self.sql(expression, "order")
    1764        measures = self.expressions(expression, key="measures")
    1765        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1766        rows = self.sql(expression, "rows")
    1767        rows = self.seg(rows) if rows else ""
    1768        after = self.sql(expression, "after")
    1769        after = self.seg(after) if after else ""
    1770        pattern = self.sql(expression, "pattern")
    1771        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1772        definition_sqls = [
    1773            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1774            for definition in expression.args.get("define", [])
    1775        ]
    1776        definitions = self.expressions(sqls=definition_sqls)
    1777        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1778        body = "".join(
    1779            (
    1780                partition,
    1781                order,
    1782                measures,
    1783                rows,
    1784                after,
    1785                pattern,
    1786                define,
    1787            )
    1788        )
    1789        alias = self.sql(expression, "alias")
    1790        alias = f" {alias}" if alias else ""
    1791        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    1793    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1794        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
    1795
    1796        # If the limit is generated as TOP, we need to ensure it's not generated twice
    1797        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
    1798
    1799        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1800            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    1801        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1802            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    1803
    1804        fetch = isinstance(limit, exp.Fetch)
    1805
    1806        offset_limit_modifiers = (
    1807            self.offset_limit_modifiers(expression, fetch, limit)
    1808            if with_offset_limit_modifiers
    1809            else []
    1810        )
    1811
    1812        return csv(
    1813            *sqls,
    1814            *[self.sql(join) for join in expression.args.get("joins") or []],
    1815            self.sql(expression, "connect"),
    1816            self.sql(expression, "match"),
    1817            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1818            self.sql(expression, "where"),
    1819            self.sql(expression, "group"),
    1820            self.sql(expression, "having"),
    1821            *self.after_having_modifiers(expression),
    1822            self.sql(expression, "order"),
    1823            *offset_limit_modifiers,
    1824            *self.after_limit_modifiers(expression),
    1825            sep="",
    1826        )

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

    1828    def offset_limit_modifiers(
    1829        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    1830    ) -> t.List[str]:
    1831        return [
    1832            self.sql(expression, "offset") if fetch else self.sql(limit),
    1833            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1834        ]

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

    1836    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1837        return [
    1838            self.sql(expression, "qualify"),
    1839            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1840            if expression.args.get("windows")
    1841            else "",
    1842            self.sql(expression, "distribute"),
    1843            self.sql(expression, "sort"),
    1844            self.sql(expression, "cluster"),
    1845        ]

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

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

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

    1852    def select_sql(self, expression: exp.Select) -> str:
    1853        hint = self.sql(expression, "hint")
    1854        distinct = self.sql(expression, "distinct")
    1855        distinct = f" {distinct}" if distinct else ""
    1856        kind = self.sql(expression, "kind").upper()
    1857        limit = expression.args.get("limit")
    1858        top = (
    1859            self.limit_sql(limit, top=True)
    1860            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
    1861            else ""
    1862        )
    1863
    1864        expressions = self.expressions(expression)
    1865
    1866        if kind:
    1867            if kind in self.SELECT_KINDS:
    1868                kind = f" AS {kind}"
    1869            else:
    1870                if kind == "STRUCT":
    1871                    expressions = self.expressions(
    1872                        sqls=[
    1873                            self.sql(
    1874                                exp.Struct(
    1875                                    expressions=[
    1876                                        exp.column(e.output_name).eq(
    1877                                            e.this if isinstance(e, exp.Alias) else e
    1878                                        )
    1879                                        for e in expression.expressions
    1880                                    ]
    1881                                )
    1882                            )
    1883                        ]
    1884                    )
    1885                kind = ""
    1886
    1887        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1888        sql = self.query_modifiers(
    1889            expression,
    1890            f"SELECT{top}{hint}{distinct}{kind}{expressions}",
    1891            self.sql(expression, "into", comment=False),
    1892            self.sql(expression, "from", comment=False),
    1893        )
    1894        return self.prepend_ctes(expression, sql)

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

    1896    def schema_sql(self, expression: exp.Schema) -> str:
    1897        this = self.sql(expression, "this")
    1898        this = f"{this} " if this else ""
    1899        sql = self.schema_columns_sql(expression)
    1900        return f"{this}{sql}"

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

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

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

    1905    def star_sql(self, expression: exp.Star) -> str:
    1906        except_ = self.expressions(expression, key="except", flat=True)
    1907        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1908        replace = self.expressions(expression, key="replace", flat=True)
    1909        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1910        return f"*{except_}{replace}"

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

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

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

    1916    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1917        this = self.sql(expression, "this")
    1918        kind = expression.text("kind")
    1919        if kind:
    1920            kind = f"{kind}."
    1921        return f"@@{kind}{this}"

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

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

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

    1926    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1927        alias = self.sql(expression, "alias")
    1928        alias = f"{sep}{alias}" if alias else ""
    1929
    1930        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1931        pivots = f" {pivots}" if pivots else ""
    1932
    1933        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    1934        return self.prepend_ctes(expression, sql)

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

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

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

    1940    def union_sql(self, expression: exp.Union) -> str:
    1941        return self.prepend_ctes(
    1942            expression,
    1943            self.set_operation(expression, self.union_op(expression)),
    1944        )

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

    1946    def union_op(self, expression: exp.Union) -> str:
    1947        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1948        kind = kind if expression.args.get("distinct") else " ALL"
    1949        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1950        return f"UNION{kind}{by_name}"

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

    1952    def unnest_sql(self, expression: exp.Unnest) -> str:
    1953        args = self.expressions(expression, flat=True)
    1954
    1955        alias = expression.args.get("alias")
    1956        offset = expression.args.get("offset")
    1957
    1958        if self.UNNEST_WITH_ORDINALITY:
    1959            if alias and isinstance(offset, exp.Expression):
    1960                alias = alias.copy()
    1961                alias.append("columns", offset.copy())
    1962
    1963        if alias and self.UNNEST_COLUMN_ONLY:
    1964            columns = alias.columns
    1965            alias = self.sql(columns[0]) if columns else ""
    1966        else:
    1967            alias = self.sql(alias)
    1968
    1969        alias = f" AS {alias}" if alias else alias
    1970        if self.UNNEST_WITH_ORDINALITY:
    1971            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    1972        else:
    1973            if isinstance(offset, exp.Expression):
    1974                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    1975            elif offset:
    1976                suffix = f"{alias} WITH OFFSET"
    1977            else:
    1978                suffix = alias
    1979
    1980        return f"UNNEST({args}){suffix}"

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

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

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

    1986    def window_sql(self, expression: exp.Window) -> str:
    1987        this = self.sql(expression, "this")
    1988        partition = self.partition_by_sql(expression)
    1989        order = expression.args.get("order")
    1990        order = self.order_sql(order, flat=True) if order else ""
    1991        spec = self.sql(expression, "spec")
    1992        alias = self.sql(expression, "alias")
    1993        over = self.sql(expression, "over") or "OVER"
    1994
    1995        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    1996
    1997        first = expression.args.get("first")
    1998        if first is None:
    1999            first = ""
    2000        else:
    2001            first = "FIRST" if first else "LAST"
    2002
    2003        if not partition and not order and not spec and alias:
    2004            return f"{this} {alias}"
    2005
    2006        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2007        return f"{this} ({args})"

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

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

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

    2013    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2014        kind = self.sql(expression, "kind")
    2015        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2016        end = (
    2017            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2018            or "CURRENT ROW"
    2019        )
    2020        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2027    def between_sql(self, expression: exp.Between) -> str:
    2028        this = self.sql(expression, "this")
    2029        low = self.sql(expression, "low")
    2030        high = self.sql(expression, "high")
    2031        return f"{this} BETWEEN {low} AND {high}"

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

    2033    def bracket_sql(self, expression: exp.Bracket) -> str:
    2034        expressions = apply_index_offset(expression.this, expression.expressions, self.INDEX_OFFSET)
    2035        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2036
    2037        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

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

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

    2045    def any_sql(self, expression: exp.Any) -> str:
    2046        this = self.sql(expression, "this")
    2047        if isinstance(expression.this, exp.Subqueryable):
    2048            this = self.wrap(this)
    2049        return f"ANY {this}"

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

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

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

    2054    def case_sql(self, expression: exp.Case) -> str:
    2055        this = self.sql(expression, "this")
    2056        statements = [f"CASE {this}" if this else "CASE"]
    2057
    2058        for e in expression.args["ifs"]:
    2059            statements.append(f"WHEN {self.sql(e, 'this')}")
    2060            statements.append(f"THEN {self.sql(e, 'true')}")
    2061
    2062        default = self.sql(expression, "default")
    2063
    2064        if default:
    2065            statements.append(f"ELSE {default}")
    2066
    2067        statements.append("END")
    2068
    2069        if self.pretty and self.text_width(statements) > self.max_text_width:
    2070            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2071
    2072        return " ".join(statements)

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

    2074    def constraint_sql(self, expression: exp.Constraint) -> str:
    2075        this = self.sql(expression, "this")
    2076        expressions = self.expressions(expression, flat=True)
    2077        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2084    def extract_sql(self, expression: exp.Extract) -> str:
    2085        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2086        expression_sql = self.sql(expression, "expression")
    2087        return f"EXTRACT({this} FROM {expression_sql})"

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

    2089    def trim_sql(self, expression: exp.Trim) -> str:
    2090        trim_type = self.sql(expression, "position")
    2091
    2092        if trim_type == "LEADING":
    2093            return self.func("LTRIM", expression.this)
    2094        elif trim_type == "TRAILING":
    2095            return self.func("RTRIM", expression.this)
    2096        else:
    2097            return self.func("TRIM", expression.this, expression.expression)

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

    2099    def safeconcat_sql(self, expression: exp.SafeConcat) -> str:
    2100        expressions = expression.expressions
    2101        if self.STRICT_STRING_CONCAT:
    2102            expressions = (exp.cast(e, "text") for e in expressions)
    2103        return self.func("CONCAT", *expressions)

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

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

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

    2109    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2110        expressions = self.expressions(expression, flat=True)
    2111        reference = self.sql(expression, "reference")
    2112        reference = f" {reference}" if reference else ""
    2113        delete = self.sql(expression, "delete")
    2114        delete = f" ON DELETE {delete}" if delete else ""
    2115        update = self.sql(expression, "update")
    2116        update = f" ON UPDATE {update}" if update else ""
    2117        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2119    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2120        expressions = self.expressions(expression, flat=True)
    2121        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2122        options = f" {options}" if options else ""
    2123        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

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

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

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

    2140    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    2141        null_handling = expression.args.get("null_handling")
    2142        null_handling = f" {null_handling}" if null_handling else ""
    2143        unique_keys = expression.args.get("unique_keys")
    2144        if unique_keys is not None:
    2145            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2146        else:
    2147            unique_keys = ""
    2148        return_type = self.sql(expression, "return_type")
    2149        return_type = f" RETURNING {return_type}" if return_type else ""
    2150        encoding = self.sql(expression, "encoding")
    2151        encoding = f" ENCODING {encoding}" if encoding else ""
    2152        return self.func(
    2153            "JSON_OBJECT",
    2154            *expression.expressions,
    2155            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2156        )

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

    2158    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2159        null_handling = expression.args.get("null_handling")
    2160        null_handling = f" {null_handling}" if null_handling else ""
    2161        return_type = self.sql(expression, "return_type")
    2162        return_type = f" RETURNING {return_type}" if return_type else ""
    2163        strict = " STRICT" if expression.args.get("strict") else ""
    2164        return self.func(
    2165            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2166        )

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

    2168    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2169        this = self.sql(expression, "this")
    2170        order = self.sql(expression, "order")
    2171        null_handling = expression.args.get("null_handling")
    2172        null_handling = f" {null_handling}" if null_handling else ""
    2173        return_type = self.sql(expression, "return_type")
    2174        return_type = f" RETURNING {return_type}" if return_type else ""
    2175        strict = " STRICT" if expression.args.get("strict") else ""
    2176        return self.func(
    2177            "JSON_ARRAYAGG",
    2178            this,
    2179            suffix=f"{order}{null_handling}{return_type}{strict})",
    2180        )

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

    2182    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2183        path = self.sql(expression, "path")
    2184        path = f" PATH {path}" if path else ""
    2185        nested_schema = self.sql(expression, "nested_schema")
    2186
    2187        if nested_schema:
    2188            return f"NESTED{path} {nested_schema}"
    2189
    2190        this = self.sql(expression, "this")
    2191        kind = self.sql(expression, "kind")
    2192        kind = f" {kind}" if kind else ""
    2193        return f"{this}{kind}{path}"

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

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

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

    2198    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2199        this = self.sql(expression, "this")
    2200        path = self.sql(expression, "path")
    2201        path = f", {path}" if path else ""
    2202        error_handling = expression.args.get("error_handling")
    2203        error_handling = f" {error_handling}" if error_handling else ""
    2204        empty_handling = expression.args.get("empty_handling")
    2205        empty_handling = f" {empty_handling}" if empty_handling else ""
    2206        schema = self.sql(expression, "schema")
    2207        return self.func(
    2208            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2209        )

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

    2211    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2212        this = self.sql(expression, "this")
    2213        kind = self.sql(expression, "kind")
    2214        path = self.sql(expression, "path")
    2215        path = f" {path}" if path else ""
    2216        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2217        return f"{this} {kind}{path}{as_json}"

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

    2219    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2220        this = self.sql(expression, "this")
    2221        path = self.sql(expression, "path")
    2222        path = f", {path}" if path else ""
    2223        expressions = self.expressions(expression)
    2224        with_ = (
    2225            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2226            if expressions
    2227            else ""
    2228        )
    2229        return f"OPENJSON({this}{path}){with_}"

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

    2231    def in_sql(self, expression: exp.In) -> str:
    2232        query = expression.args.get("query")
    2233        unnest = expression.args.get("unnest")
    2234        field = expression.args.get("field")
    2235        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2236
    2237        if query:
    2238            in_sql = self.wrap(query)
    2239        elif unnest:
    2240            in_sql = self.in_unnest_op(unnest)
    2241        elif field:
    2242            in_sql = self.sql(field)
    2243        else:
    2244            in_sql = f"({self.expressions(expression, flat=True)})"
    2245
    2246        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2251    def interval_sql(self, expression: exp.Interval) -> str:
    2252        unit = self.sql(expression, "unit")
    2253        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2254            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    2255        unit = f" {unit}" if unit else ""
    2256
    2257        if self.SINGLE_STRING_INTERVAL:
    2258            this = expression.this.name if expression.this else ""
    2259            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2260
    2261        this = self.sql(expression, "this")
    2262        if this:
    2263            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2264            this = f" {this}" if unwrapped else f" ({this})"
    2265
    2266        return f"INTERVAL{this}{unit}"

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

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

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

    2271    def reference_sql(self, expression: exp.Reference) -> str:
    2272        this = self.sql(expression, "this")
    2273        expressions = self.expressions(expression, flat=True)
    2274        expressions = f"({expressions})" if expressions else ""
    2275        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2276        options = f" {options}" if options else ""
    2277        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    2282    def paren_sql(self, expression: exp.Paren) -> str:
    2283        if isinstance(expression.unnest(), exp.Select):
    2284            sql = self.wrap(expression)
    2285        else:
    2286            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2287            sql = f"({sql}{self.seg(')', sep='')}"
    2288
    2289        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    2300    def alias_sql(self, expression: exp.Alias) -> str:
    2301        alias = self.sql(expression, "alias")
    2302        alias = f" AS {alias}" if alias else ""
    2303        return f"{self.sql(expression, 'this')}{alias}"

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

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

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

    2308    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2309        this = self.sql(expression, "this")
    2310        zone = self.sql(expression, "zone")
    2311        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

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

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

    2322    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2323        if not self.pretty:
    2324            return self.binary(expression, op)
    2325
    2326        sqls = tuple(
    2327            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2328            for i, e in enumerate(expression.flatten(unnest=False))
    2329        )
    2330
    2331        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2332        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2352    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2353        format_sql = self.sql(expression, "format")
    2354        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2355        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

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

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

    2361    def collate_sql(self, expression: exp.Collate) -> str:
    2362        if self.COLLATE_IS_FUNC:
    2363            return self.function_fallback_sql(expression)
    2364        return self.binary(expression, "COLLATE")

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

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

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

    2369    def comment_sql(self, expression: exp.Comment) -> str:
    2370        this = self.sql(expression, "this")
    2371        kind = expression.args["kind"]
    2372        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2373        expression_sql = self.sql(expression, "expression")
    2374        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    2376    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2377        this = self.sql(expression, "this")
    2378        delete = " DELETE" if expression.args.get("delete") else ""
    2379        recompress = self.sql(expression, "recompress")
    2380        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2381        to_disk = self.sql(expression, "to_disk")
    2382        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2383        to_volume = self.sql(expression, "to_volume")
    2384        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2385        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    2387    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2388        where = self.sql(expression, "where")
    2389        group = self.sql(expression, "group")
    2390        aggregates = self.expressions(expression, key="aggregates")
    2391        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2392
    2393        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2394            return f"TTL {self.expressions(expression, flat=True)}"
    2395
    2396        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2401    def commit_sql(self, expression: exp.Commit) -> str:
    2402        chain = expression.args.get("chain")
    2403        if chain is not None:
    2404            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2405
    2406        return f"COMMIT{chain or ''}"

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

    2408    def rollback_sql(self, expression: exp.Rollback) -> str:
    2409        savepoint = expression.args.get("savepoint")
    2410        savepoint = f" TO {savepoint}" if savepoint else ""
    2411        return f"ROLLBACK{savepoint}"

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

    2413    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2414        this = self.sql(expression, "this")
    2415
    2416        dtype = self.sql(expression, "dtype")
    2417        if dtype:
    2418            collate = self.sql(expression, "collate")
    2419            collate = f" COLLATE {collate}" if collate else ""
    2420            using = self.sql(expression, "using")
    2421            using = f" USING {using}" if using else ""
    2422            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2423
    2424        default = self.sql(expression, "default")
    2425        if default:
    2426            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2427
    2428        if not expression.args.get("drop"):
    2429            self.unsupported("Unsupported ALTER COLUMN syntax")
    2430
    2431        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2433    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2434        if not self.RENAME_TABLE_WITH_DB:
    2435            # Remove db from tables
    2436            expression = expression.transform(
    2437                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2438            )
    2439        this = self.sql(expression, "this")
    2440        return f"RENAME TO {this}"

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

    2442    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2443        actions = expression.args["actions"]
    2444
    2445        if isinstance(actions[0], exp.ColumnDef):
    2446            if self.ALTER_TABLE_ADD_COLUMN_KEYWORD:
    2447                actions = self.expressions(
    2448                    expression,
    2449                    key="actions",
    2450                    prefix="ADD COLUMN ",
    2451                )
    2452            else:
    2453                actions = f"ADD {self.expressions(expression, key='actions')}"
    2454        elif isinstance(actions[0], exp.Schema):
    2455            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2456        elif isinstance(actions[0], exp.Delete):
    2457            actions = self.expressions(expression, key="actions", flat=True)
    2458        else:
    2459            actions = self.expressions(expression, key="actions", flat=True)
    2460
    2461        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2462        only = " ONLY" if expression.args.get("only") else ""
    2463        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"

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

    2465    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2466        expressions = self.expressions(expression)
    2467        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2468        return f"DROP{exists}{expressions}"

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

    2470    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2471        this = self.sql(expression, "this")
    2472        expression_ = self.sql(expression, "expression")
    2473        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2474
    2475        enforced = expression.args.get("enforced")
    2476        if enforced is not None:
    2477            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2478
    2479        return f"{add_constraint} {expression_}"

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

    2481    def distinct_sql(self, expression: exp.Distinct) -> str:
    2482        this = self.expressions(expression, flat=True)
    2483        this = f" {this}" if this else ""
    2484
    2485        on = self.sql(expression, "on")
    2486        on = f" ON {on}" if on else ""
    2487        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2495    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2496        return self.sql(
    2497            exp.Cast(
    2498                this=exp.Div(this=expression.this.copy(), expression=expression.expression.copy()),
    2499                to=exp.DataType(this=exp.DataType.Type.INT),
    2500            )
    2501        )

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

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

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

    2506    def safedpipe_sql(self, expression: exp.SafeDPipe) -> str:
    2507        if self.STRICT_STRING_CONCAT:
    2508            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
    2509        return self.dpipe_sql(expression)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2544    def is_sql(self, expression: exp.Is) -> str:
    2545        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    2546            return self.sql(
    2547                expression.this if expression.expression.this else exp.not_(expression.this)
    2548            )
    2549        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2593    def log_sql(self, expression: exp.Log) -> str:
    2594        args = list(expression.args.values())
    2595        if not self.LOG_BASE_FIRST:
    2596            args.reverse()
    2597        return self.func("LOG", *args)

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

    2599    def use_sql(self, expression: exp.Use) -> str:
    2600        kind = self.sql(expression, "kind")
    2601        kind = f" {kind}" if kind else ""
    2602        this = self.sql(expression, "this")
    2603        this = f" {this}" if this else ""
    2604        return f"USE{kind}{this}"

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

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

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

    2610    def function_fallback_sql(self, expression: exp.Func) -> str:
    2611        args = []
    2612
    2613        for key in expression.arg_types:
    2614            arg_value = expression.args.get(key)
    2615
    2616            if isinstance(arg_value, list):
    2617                for value in arg_value:
    2618                    args.append(value)
    2619            elif arg_value is not None:
    2620                args.append(arg_value)
    2621
    2622        if self.normalize_functions:
    2623            name = expression.sql_name()
    2624        else:
    2625            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    2626
    2627        return self.func(name, *args)

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

    2629    def func(
    2630        self,
    2631        name: str,
    2632        *args: t.Optional[exp.Expression | str],
    2633        prefix: str = "(",
    2634        suffix: str = ")",
    2635    ) -> str:
    2636        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

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

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

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

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

    2647    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2648        return format_time(
    2649            self.sql(expression, "format"), self.INVERSE_TIME_MAPPING, self.INVERSE_TIME_TRIE
    2650        )

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

    2652    def expressions(
    2653        self,
    2654        expression: t.Optional[exp.Expression] = None,
    2655        key: t.Optional[str] = None,
    2656        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    2657        flat: bool = False,
    2658        indent: bool = True,
    2659        skip_first: bool = False,
    2660        sep: str = ", ",
    2661        prefix: str = "",
    2662    ) -> str:
    2663        expressions = expression.args.get(key or "expressions") if expression else sqls
    2664
    2665        if not expressions:
    2666            return ""
    2667
    2668        if flat:
    2669            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    2670
    2671        num_sqls = len(expressions)
    2672
    2673        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2674        pad = " " * self.pad
    2675        stripped_sep = sep.strip()
    2676
    2677        result_sqls = []
    2678        for i, e in enumerate(expressions):
    2679            sql = self.sql(e, comment=False)
    2680            if not sql:
    2681                continue
    2682
    2683            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2684
    2685            if self.pretty:
    2686                if self.leading_comma:
    2687                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2688                else:
    2689                    result_sqls.append(
    2690                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2691                    )
    2692            else:
    2693                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2694
    2695        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2696        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

    2698    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2699        flat = flat or isinstance(expression.parent, exp.Properties)
    2700        expressions_sql = self.expressions(expression, flat=flat)
    2701        if flat:
    2702            return f"{op} {expressions_sql}"
    2703        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    2705    def naked_property(self, expression: exp.Property) -> str:
    2706        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2707        if not property_name:
    2708            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2709        return f"{property_name} {self.sql(expression, 'this')}"

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

    2711    def set_operation(self, expression: exp.Expression, op: str) -> str:
    2712        this = self.sql(expression, "this")
    2713        op = self.seg(op)
    2714        return self.query_modifiers(
    2715            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2716        )

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

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

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

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

    2724    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2725        this = self.sql(expression, "this")
    2726        expressions = self.no_identify(self.expressions, expression)
    2727        expressions = (
    2728            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2729        )
    2730        return f"{this}{expressions}"

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

    2732    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2733        this = self.sql(expression, "this")
    2734        expressions = self.expressions(expression, flat=True)
    2735        return f"{this}({expressions})"

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

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

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

    2740    def when_sql(self, expression: exp.When) -> str:
    2741        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2742        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2743        condition = self.sql(expression, "condition")
    2744        condition = f" AND {condition}" if condition else ""
    2745
    2746        then_expression = expression.args.get("then")
    2747        if isinstance(then_expression, exp.Insert):
    2748            then = f"INSERT {self.sql(then_expression, 'this')}"
    2749            if "expression" in then_expression.args:
    2750                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2751        elif isinstance(then_expression, exp.Update):
    2752            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2753                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2754            else:
    2755                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2756        else:
    2757            then = self.sql(then_expression)
    2758        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    2760    def merge_sql(self, expression: exp.Merge) -> str:
    2761        table = expression.this
    2762        table_alias = ""
    2763
    2764        hints = table.args.get("hints")
    2765        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    2766            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    2767            table = table.copy()
    2768            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    2769
    2770        this = self.sql(table)
    2771        using = f"USING {self.sql(expression, 'using')}"
    2772        on = f"ON {self.sql(expression, 'on')}"
    2773        expressions = self.expressions(expression, sep=" ")
    2774
    2775        return f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"

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

    2777    def tochar_sql(self, expression: exp.ToChar) -> str:
    2778        if expression.args.get("format"):
    2779            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2780
    2781        return self.sql(exp.cast(expression.this, "text"))

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

    2783    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2784        this = self.sql(expression, "this")
    2785        kind = self.sql(expression, "kind")
    2786        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2787        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2788        return f"{this}({kind}{args})"

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

    2790    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2791        this = self.sql(expression, "this")
    2792        max = self.sql(expression, "max")
    2793        min = self.sql(expression, "min")
    2794        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    2802    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    2803        expressions = self.expressions(expression, key="expressions", flat=True)
    2804        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    2805        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    2806        buckets = self.sql(expression, "buckets")
    2807        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    2809    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    2810        this = self.sql(expression, "this")
    2811        having = self.sql(expression, "having")
    2812
    2813        if having:
    2814            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    2815
    2816        return self.func("ANY_VALUE", this)

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

    2818    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    2819        transform = self.func("TRANSFORM", *expression.expressions)
    2820        row_format_before = self.sql(expression, "row_format_before")
    2821        row_format_before = f" {row_format_before}" if row_format_before else ""
    2822        record_writer = self.sql(expression, "record_writer")
    2823        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    2824        using = f" USING {self.sql(expression, 'command_script')}"
    2825        schema = self.sql(expression, "schema")
    2826        schema = f" AS {schema}" if schema else ""
    2827        row_format_after = self.sql(expression, "row_format_after")
    2828        row_format_after = f" {row_format_after}" if row_format_after else ""
    2829        record_reader = self.sql(expression, "record_reader")
    2830        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    2831        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

    2833    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    2834        key_block_size = self.sql(expression, "key_block_size")
    2835        if key_block_size:
    2836            return f"KEY_BLOCK_SIZE = {key_block_size}"
    2837
    2838        using = self.sql(expression, "using")
    2839        if using:
    2840            return f"USING {using}"
    2841
    2842        parser = self.sql(expression, "parser")
    2843        if parser:
    2844            return f"WITH PARSER {parser}"
    2845
    2846        comment = self.sql(expression, "comment")
    2847        if comment:
    2848            return f"COMMENT {comment}"
    2849
    2850        visible = expression.args.get("visible")
    2851        if visible is not None:
    2852            return "VISIBLE" if visible else "INVISIBLE"
    2853
    2854        engine_attr = self.sql(expression, "engine_attr")
    2855        if engine_attr:
    2856            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    2857
    2858        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    2859        if secondary_engine_attr:
    2860            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    2861
    2862        self.unsupported("Unsupported index constraint option.")
    2863        return ""

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

    2865    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    2866        kind = self.sql(expression, "kind")
    2867        kind = f"{kind} INDEX" if kind else "INDEX"
    2868        this = self.sql(expression, "this")
    2869        this = f" {this}" if this else ""
    2870        index_type = self.sql(expression, "index_type")
    2871        index_type = f" USING {index_type}" if index_type else ""
    2872        schema = self.sql(expression, "schema")
    2873        schema = f" {schema}" if schema else ""
    2874        options = self.expressions(expression, key="options", sep=" ")
    2875        options = f" {options}" if options else ""
    2876        return f"{kind}{this}{index_type}{schema}{options}"

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

    2878    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    2879        if self.NVL2_SUPPORTED:
    2880            return self.function_fallback_sql(expression)
    2881
    2882        case = exp.Case().when(
    2883            expression.this.is_(exp.null()).not_(copy=False),
    2884            expression.args["true"].copy(),
    2885            copy=False,
    2886        )
    2887        else_cond = expression.args.get("false")
    2888        if else_cond:
    2889            case.else_(else_cond.copy(), copy=False)
    2890
    2891        return self.sql(case)

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

    2893    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    2894        this = self.sql(expression, "this")
    2895        expr = self.sql(expression, "expression")
    2896        iterator = self.sql(expression, "iterator")
    2897        condition = self.sql(expression, "condition")
    2898        condition = f" IF {condition}" if condition else ""
    2899        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    2907    def predict_sql(self, expression: exp.Predict) -> str:
    2908        model = self.sql(expression, "this")
    2909        model = f"MODEL {model}"
    2910        table = self.sql(expression, "expression")
    2911        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    2912        parameters = self.sql(expression, "params_struct")
    2913        return self.func("PREDICT", model, table, parameters or None)

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

    2916def cached_generator(
    2917    cache: t.Optional[t.Dict[int, str]] = None
    2918) -> t.Callable[[exp.Expression], str]:
    2919    """Returns a cached generator."""
    2920    cache = {} if cache is None else cache
    2921    generator = Generator(normalize=True, identify="safe")
    2922    return lambda e: generator.generate(e, cache)

Returns a cached generator.