sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

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

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

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

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

###### Arguments:

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

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

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

TRANSFORMS = {<class 'sqlglot.expressions.DateAdd'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CheckColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED = True
LOCKING_READS_SUPPORTED = False
EXPLICIT_UNION = False
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SIZE_IS_PERCENT = False
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
COLUMN_JOIN_MARKS_SUPPORTED = False
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
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.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
KEY_VALUE_DEFINITONS = (<class 'sqlglot.expressions.Bracket'>, <class 'sqlglot.expressions.EQ'>, <class 'sqlglot.expressions.PropertyEQ'>, <class 'sqlglot.expressions.Slice'>)
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str: View Source

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

Generates the SQL string corresponding to the given syntax tree.

###### Arguments:

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

###### Returns:

> The SQL string corresponding to `expression`.

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

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

Apply generic preprocessing transformations to a given expression.

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

    485    def unsupported(self, message: str) -> None:
    486        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    487            raise UnsupportedError(message)
    488        self.unsupported_messages.append(message)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    859    def clone_sql(self, expression: exp.Clone) -> str:
    860        this = self.sql(expression, "this")
    861        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    862        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    863        this = f"{shallow}{keyword} {this}"
    864        when = self.sql(expression, "when")
    865
    866        if when:
    867            kind = self.sql(expression, "kind")
    868            expr = self.sql(expression, "expression")
    869            return f"{this} {when} ({kind} => {expr})"
    870
    871        return this

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

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

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

    876    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    877        with_ = self.sql(expression, "with")
    878        if with_:
    879            sql = f"{with_}{self.sep()}{sql}"
    880        return sql

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

    882    def with_sql(self, expression: exp.With) -> str:
    883        sql = self.expressions(expression, flat=True)
    884        recursive = (
    885            "RECURSIVE "
    886            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    887            else ""
    888        )
    889
    890        return f"WITH {recursive}{sql}"

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

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

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

    896    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    897        alias = self.sql(expression, "this")
    898        columns = self.expressions(expression, key="columns", flat=True)
    899        columns = f"({columns})" if columns else ""
    900
    901        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    902            alias = "_t"
    903
    904        return f"{alias}{columns}"

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

    906    def bitstring_sql(self, expression: exp.BitString) -> str:
    907        this = self.sql(expression, "this")
    908        if self.dialect.BIT_START:
    909            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    910        return f"{int(this, 2)}"

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

    912    def hexstring_sql(self, expression: exp.HexString) -> str:
    913        this = self.sql(expression, "this")
    914        if self.dialect.HEX_START:
    915            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    916        return f"{int(this, 16)}"

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

    918    def bytestring_sql(self, expression: exp.ByteString) -> str:
    919        this = self.sql(expression, "this")
    920        if self.dialect.BYTE_START:
    921            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    922        return this

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

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

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

    928    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    929        this = self.sql(expression, "this")
    930        specifier = self.sql(expression, "expression")
    931        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    932        return f"{this}{specifier}"

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

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

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

    971    def directory_sql(self, expression: exp.Directory) -> str:
    972        local = "LOCAL " if expression.args.get("local") else ""
    973        row_format = self.sql(expression, "row_format")
    974        row_format = f" {row_format}" if row_format else ""
    975        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

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

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

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

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

    1006    def except_sql(self, expression: exp.Except) -> str:
    1007        return self.prepend_ctes(
    1008            expression,
    1009            self.set_operation(expression, self.except_op(expression)),
    1010        )

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

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

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

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

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

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

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

    1037    def hint_sql(self, expression: exp.Hint) -> str:
    1038        if not self.QUERY_HINTS:
    1039            self.unsupported("Hints are not supported")
    1040            return ""
    1041
    1042        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

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

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

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

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

    1076    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1077        input_format = self.sql(expression, "input_format")
    1078        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1079        output_format = self.sql(expression, "output_format")
    1080        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1081        return self.sep().join((input_format, output_format))

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

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

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

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

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

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

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

    1105    def root_properties(self, properties: exp.Properties) -> str:
    1106        if properties.expressions:
    1107            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1108        return ""

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

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

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

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

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

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

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

    1139    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1140        if isinstance(expression.this, exp.Dot):
    1141            return self.sql(expression, "this")
    1142        return f"'{expression.name}'" if string_key else expression.name

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

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

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

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

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

    1160    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1161        no = "NO " if expression.args.get("no") else ""
    1162        protection = " PROTECTION" if expression.args.get("protection") else ""
    1163        return f"{no}FALLBACK{protection}"

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

    1165    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1166        no = "NO " if expression.args.get("no") else ""
    1167        local = expression.args.get("local")
    1168        local = f"{local} " if local else ""
    1169        dual = "DUAL " if expression.args.get("dual") else ""
    1170        before = "BEFORE " if expression.args.get("before") else ""
    1171        after = "AFTER " if expression.args.get("after") else ""
    1172        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1174    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1175        freespace = self.sql(expression, "this")
    1176        percent = " PERCENT" if expression.args.get("percent") else ""
    1177        return f"FREESPACE={freespace}{percent}"

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

    1179    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1180        if expression.args.get("default"):
    1181            property = "DEFAULT"
    1182        elif expression.args.get("on"):
    1183            property = "ON"
    1184        else:
    1185            property = "OFF"
    1186        return f"CHECKSUM={property}"

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

    1188    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1189        if expression.args.get("no"):
    1190            return "NO MERGEBLOCKRATIO"
    1191        if expression.args.get("default"):
    1192            return "DEFAULT MERGEBLOCKRATIO"
    1193
    1194        percent = " PERCENT" if expression.args.get("percent") else ""
    1195        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

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

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

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

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

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

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

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

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

    1259    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1260        this = self.sql(expression, "this")
    1261
    1262        for_values_or_default = expression.expression
    1263        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1264            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1265        else:
    1266            for_values_or_default = " DEFAULT"
    1267
    1268        return f"PARTITION OF {this}{for_values_or_default}"

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

    1270    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1271        kind = expression.args.get("kind")
    1272        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1273        for_or_in = expression.args.get("for_or_in")
    1274        for_or_in = f" {for_or_in}" if for_or_in else ""
    1275        lock_type = expression.args.get("lock_type")
    1276        override = " OVERRIDE" if expression.args.get("override") else ""
    1277        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1279    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1280        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1281        statistics = expression.args.get("statistics")
    1282        statistics_sql = ""
    1283        if statistics is not None:
    1284            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1285        return f"{data_sql}{statistics_sql}"

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

    1287    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1288        sql = "WITH(SYSTEM_VERSIONING=ON"
    1289
    1290        if expression.this:
    1291            history_table = self.sql(expression, "this")
    1292            sql = f"{sql}(HISTORY_TABLE={history_table}"
    1293
    1294            if expression.expression:
    1295                data_consistency_check = self.sql(expression, "expression")
    1296                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
    1297
    1298            sql = f"{sql})"
    1299
    1300        return f"{sql})"

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

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

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

    1335    def intersect_sql(self, expression: exp.Intersect) -> str:
    1336        return self.prepend_ctes(
    1337            expression,
    1338            self.set_operation(expression, self.intersect_op(expression)),
    1339        )

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

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

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

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

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

    1347    def kill_sql(self, expression: exp.Kill) -> str:
    1348        kind = self.sql(expression, "kind")
    1349        kind = f" {kind}" if kind else ""
    1350        this = self.sql(expression, "this")
    1351        this = f" {this}" if this else ""
    1352        return f"KILL{kind}{this}"

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

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

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

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

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

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

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

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

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

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

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

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

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

    1395    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1396        this = f"{self.sql(expression, 'this')} INDEX"
    1397        target = self.sql(expression, "target")
    1398        target = f" FOR {target}" if target else ""
    1399        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

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

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

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

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

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

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

    1503    def version_sql(self, expression: exp.Version) -> str:
    1504        this = f"FOR {expression.name}"
    1505        kind = expression.text("kind")
    1506        expr = self.sql(expression, "expression")
    1507        return f"{this} {kind} {expr}"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1622    def connect_sql(self, expression: exp.Connect) -> str:
    1623        start = self.sql(expression, "start")
    1624        start = self.seg(f"START WITH {start}") if start else ""
    1625        connect = self.sql(expression, "connect")
    1626        connect = self.seg(f"CONNECT BY {connect}")
    1627        return start + connect

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

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

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

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

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

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

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

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

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

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

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

    1703    def offset_sql(self, expression: exp.Offset) -> str:
    1704        this = self.sql(expression, "this")
    1705        expression = expression.expression
    1706        expression = (
    1707            self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression
    1708        )
    1709        return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"

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

    1711    def setitem_sql(self, expression: exp.SetItem) -> str:
    1712        kind = self.sql(expression, "kind")
    1713        kind = f"{kind} " if kind else ""
    1714        this = self.sql(expression, "this")
    1715        expressions = self.expressions(expression)
    1716        collate = self.sql(expression, "collate")
    1717        collate = f" COLLATE {collate}" if collate else ""
    1718        global_ = "GLOBAL " if expression.args.get("global") else ""
    1719        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1721    def set_sql(self, expression: exp.Set) -> str:
    1722        expressions = (
    1723            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1724        )
    1725        tag = " TAG" if expression.args.get("tag") else ""
    1726        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

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

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

    1749    def literal_sql(self, expression: exp.Literal) -> str:
    1750        text = expression.this or ""
    1751        if expression.is_string:
    1752            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    1753        return text

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

    1755    def escape_str(self, text: str) -> str:
    1756        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
    1757        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
    1758            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1759        elif self.pretty:
    1760            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1761        return text

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

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

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

    1776    def null_sql(self, *_) -> str:
    1777        return "NULL"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1896    def offset_limit_modifiers(
    1897        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    1898    ) -> t.List[str]:
    1899        return [
    1900            self.sql(expression, "offset") if fetch else self.sql(limit),
    1901            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1902        ]

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

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

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

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

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

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

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

    1967    def schema_sql(self, expression: exp.Schema) -> str:
    1968        this = self.sql(expression, "this")
    1969        sql = self.schema_columns_sql(expression)
    1970        return f"{this} {sql}" if this and sql else this or sql

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

    1972    def schema_columns_sql(self, expression: exp.Schema) -> str:
    1973        if expression.expressions:
    1974            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1975        return ""

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

    1977    def star_sql(self, expression: exp.Star) -> str:
    1978        except_ = self.expressions(expression, key="except", flat=True)
    1979        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1980        replace = self.expressions(expression, key="replace", flat=True)
    1981        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1982        return f"*{except_}{replace}"

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

    1984    def parameter_sql(self, expression: exp.Parameter) -> str:
    1985        this = self.sql(expression, "this")
    1986        return f"{self.PARAMETER_TOKEN}{this}"

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

    1988    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1989        this = self.sql(expression, "this")
    1990        kind = expression.text("kind")
    1991        if kind:
    1992            kind = f"{kind}."
    1993        return f"@@{kind}{this}"

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

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

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

    1998    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1999        alias = self.sql(expression, "alias")
    2000        alias = f"{sep}{alias}" if alias else ""
    2001
    2002        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2003        pivots = f" {pivots}" if pivots else ""
    2004
    2005        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2006        return self.prepend_ctes(expression, sql)

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

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

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

    2012    def union_sql(self, expression: exp.Union) -> str:
    2013        return self.prepend_ctes(
    2014            expression,
    2015            self.set_operation(expression, self.union_op(expression)),
    2016        )

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

    2018    def union_op(self, expression: exp.Union) -> str:
    2019        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2020        kind = kind if expression.args.get("distinct") else " ALL"
    2021        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2022        return f"UNION{kind}{by_name}"

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

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

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

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

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

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

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

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

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

    2084    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2085        kind = self.sql(expression, "kind")
    2086        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2087        end = (
    2088            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2089            or "CURRENT ROW"
    2090        )
    2091        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2098    def between_sql(self, expression: exp.Between) -> str:
    2099        this = self.sql(expression, "this")
    2100        low = self.sql(expression, "low")
    2101        high = self.sql(expression, "high")
    2102        return f"{this} BETWEEN {low} AND {high}"

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

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

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

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

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

    2116    def any_sql(self, expression: exp.Any) -> str:
    2117        this = self.sql(expression, "this")
    2118        if isinstance(expression.this, exp.Subqueryable):
    2119            this = self.wrap(this)
    2120        return f"ANY {this}"

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

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

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

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

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

    2145    def constraint_sql(self, expression: exp.Constraint) -> str:
    2146        this = self.sql(expression, "this")
    2147        expressions = self.expressions(expression, flat=True)
    2148        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2155    def extract_sql(self, expression: exp.Extract) -> str:
    2156        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2157        expression_sql = self.sql(expression, "expression")
    2158        return f"EXTRACT({this} FROM {expression_sql})"

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

    2160    def trim_sql(self, expression: exp.Trim) -> str:
    2161        trim_type = self.sql(expression, "position")
    2162
    2163        if trim_type == "LEADING":
    2164            return self.func("LTRIM", expression.this)
    2165        elif trim_type == "TRAILING":
    2166            return self.func("RTRIM", expression.this)
    2167        else:
    2168            return self.func("TRIM", expression.this, expression.expression)

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

    2170    def concat_sql(self, expression: exp.Concat) -> str:
    2171        expressions = expression.expressions
    2172        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2173            expressions = [exp.cast(e, "text") for e in expressions]
    2174        return self.func("CONCAT", *expressions)

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

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

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

    2180    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2181        expressions = self.expressions(expression, flat=True)
    2182        reference = self.sql(expression, "reference")
    2183        reference = f" {reference}" if reference else ""
    2184        delete = self.sql(expression, "delete")
    2185        delete = f" ON DELETE {delete}" if delete else ""
    2186        update = self.sql(expression, "update")
    2187        update = f" ON UPDATE {update}" if update else ""
    2188        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2190    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2191        expressions = self.expressions(expression, flat=True)
    2192        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2193        options = f" {options}" if options else ""
    2194        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

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

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

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

    2210    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    2211        null_handling = expression.args.get("null_handling")
    2212        null_handling = f" {null_handling}" if null_handling else ""
    2213        unique_keys = expression.args.get("unique_keys")
    2214        if unique_keys is not None:
    2215            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2216        else:
    2217            unique_keys = ""
    2218        return_type = self.sql(expression, "return_type")
    2219        return_type = f" RETURNING {return_type}" if return_type else ""
    2220        encoding = self.sql(expression, "encoding")
    2221        encoding = f" ENCODING {encoding}" if encoding else ""
    2222        return self.func(
    2223            "JSON_OBJECT",
    2224            *expression.expressions,
    2225            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2226        )

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

    2228    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2229        null_handling = expression.args.get("null_handling")
    2230        null_handling = f" {null_handling}" if null_handling else ""
    2231        return_type = self.sql(expression, "return_type")
    2232        return_type = f" RETURNING {return_type}" if return_type else ""
    2233        strict = " STRICT" if expression.args.get("strict") else ""
    2234        return self.func(
    2235            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2236        )

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

    2238    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2239        this = self.sql(expression, "this")
    2240        order = self.sql(expression, "order")
    2241        null_handling = expression.args.get("null_handling")
    2242        null_handling = f" {null_handling}" if null_handling else ""
    2243        return_type = self.sql(expression, "return_type")
    2244        return_type = f" RETURNING {return_type}" if return_type else ""
    2245        strict = " STRICT" if expression.args.get("strict") else ""
    2246        return self.func(
    2247            "JSON_ARRAYAGG",
    2248            this,
    2249            suffix=f"{order}{null_handling}{return_type}{strict})",
    2250        )

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

    2252    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2253        path = self.sql(expression, "path")
    2254        path = f" PATH {path}" if path else ""
    2255        nested_schema = self.sql(expression, "nested_schema")
    2256
    2257        if nested_schema:
    2258            return f"NESTED{path} {nested_schema}"
    2259
    2260        this = self.sql(expression, "this")
    2261        kind = self.sql(expression, "kind")
    2262        kind = f" {kind}" if kind else ""
    2263        return f"{this}{kind}{path}"

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

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

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

    2268    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2269        this = self.sql(expression, "this")
    2270        path = self.sql(expression, "path")
    2271        path = f", {path}" if path else ""
    2272        error_handling = expression.args.get("error_handling")
    2273        error_handling = f" {error_handling}" if error_handling else ""
    2274        empty_handling = expression.args.get("empty_handling")
    2275        empty_handling = f" {empty_handling}" if empty_handling else ""
    2276        schema = self.sql(expression, "schema")
    2277        return self.func(
    2278            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2279        )

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

    2281    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2282        this = self.sql(expression, "this")
    2283        kind = self.sql(expression, "kind")
    2284        path = self.sql(expression, "path")
    2285        path = f" {path}" if path else ""
    2286        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2287        return f"{this} {kind}{path}{as_json}"

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

    2289    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2290        this = self.sql(expression, "this")
    2291        path = self.sql(expression, "path")
    2292        path = f", {path}" if path else ""
    2293        expressions = self.expressions(expression)
    2294        with_ = (
    2295            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2296            if expressions
    2297            else ""
    2298        )
    2299        return f"OPENJSON({this}{path}){with_}"

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

    2301    def in_sql(self, expression: exp.In) -> str:
    2302        query = expression.args.get("query")
    2303        unnest = expression.args.get("unnest")
    2304        field = expression.args.get("field")
    2305        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2306
    2307        if query:
    2308            in_sql = self.wrap(query)
    2309        elif unnest:
    2310            in_sql = self.in_unnest_op(unnest)
    2311        elif field:
    2312            in_sql = self.sql(field)
    2313        else:
    2314            in_sql = f"({self.expressions(expression, flat=True)})"
    2315
    2316        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2321    def interval_sql(self, expression: exp.Interval) -> str:
    2322        unit = self.sql(expression, "unit")
    2323        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2324            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    2325        unit = f" {unit}" if unit else ""
    2326
    2327        if self.SINGLE_STRING_INTERVAL:
    2328            this = expression.this.name if expression.this else ""
    2329            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2330
    2331        this = self.sql(expression, "this")
    2332        if this:
    2333            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2334            this = f" {this}" if unwrapped else f" ({this})"
    2335
    2336        return f"INTERVAL{this}{unit}"

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

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

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

    2341    def reference_sql(self, expression: exp.Reference) -> str:
    2342        this = self.sql(expression, "this")
    2343        expressions = self.expressions(expression, flat=True)
    2344        expressions = f"({expressions})" if expressions else ""
    2345        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2346        options = f" {options}" if options else ""
    2347        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    2352    def paren_sql(self, expression: exp.Paren) -> str:
    2353        if isinstance(expression.unnest(), exp.Select):
    2354            sql = self.wrap(expression)
    2355        else:
    2356            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2357            sql = f"({sql}{self.seg(')', sep='')}"
    2358
    2359        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    2370    def alias_sql(self, expression: exp.Alias) -> str:
    2371        alias = self.sql(expression, "alias")
    2372        alias = f" AS {alias}" if alias else ""
    2373        return f"{self.sql(expression, 'this')}{alias}"

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

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

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

    2378    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2379        this = self.sql(expression, "this")
    2380        zone = self.sql(expression, "zone")
    2381        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

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

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

    2392    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2393        if not self.pretty:
    2394            return self.binary(expression, op)
    2395
    2396        sqls = tuple(
    2397            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2398            for i, e in enumerate(expression.flatten(unnest=False))
    2399        )
    2400
    2401        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2402        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2422    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2423        format_sql = self.sql(expression, "format")
    2424        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2425        to_sql = self.sql(expression, "to")
    2426        to_sql = f" {to_sql}" if to_sql else ""
    2427        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"

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

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

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

    2433    def collate_sql(self, expression: exp.Collate) -> str:
    2434        if self.COLLATE_IS_FUNC:
    2435            return self.function_fallback_sql(expression)
    2436        return self.binary(expression, "COLLATE")

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

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

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

    2441    def comment_sql(self, expression: exp.Comment) -> str:
    2442        this = self.sql(expression, "this")
    2443        kind = expression.args["kind"]
    2444        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2445        expression_sql = self.sql(expression, "expression")
    2446        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    2448    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2449        this = self.sql(expression, "this")
    2450        delete = " DELETE" if expression.args.get("delete") else ""
    2451        recompress = self.sql(expression, "recompress")
    2452        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2453        to_disk = self.sql(expression, "to_disk")
    2454        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2455        to_volume = self.sql(expression, "to_volume")
    2456        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2457        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    2459    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2460        where = self.sql(expression, "where")
    2461        group = self.sql(expression, "group")
    2462        aggregates = self.expressions(expression, key="aggregates")
    2463        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2464
    2465        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2466            return f"TTL {self.expressions(expression, flat=True)}"
    2467
    2468        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2473    def commit_sql(self, expression: exp.Commit) -> str:
    2474        chain = expression.args.get("chain")
    2475        if chain is not None:
    2476            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2477
    2478        return f"COMMIT{chain or ''}"

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

    2480    def rollback_sql(self, expression: exp.Rollback) -> str:
    2481        savepoint = expression.args.get("savepoint")
    2482        savepoint = f" TO {savepoint}" if savepoint else ""
    2483        return f"ROLLBACK{savepoint}"

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

    2485    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2486        this = self.sql(expression, "this")
    2487
    2488        dtype = self.sql(expression, "dtype")
    2489        if dtype:
    2490            collate = self.sql(expression, "collate")
    2491            collate = f" COLLATE {collate}" if collate else ""
    2492            using = self.sql(expression, "using")
    2493            using = f" USING {using}" if using else ""
    2494            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2495
    2496        default = self.sql(expression, "default")
    2497        if default:
    2498            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2499
    2500        if not expression.args.get("drop"):
    2501            self.unsupported("Unsupported ALTER COLUMN syntax")
    2502
    2503        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2505    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2506        if not self.RENAME_TABLE_WITH_DB:
    2507            # Remove db from tables
    2508            expression = expression.transform(
    2509                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2510            )
    2511        this = self.sql(expression, "this")
    2512        return f"RENAME TO {this}"

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

    2514    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2515        actions = expression.args["actions"]
    2516
    2517        if isinstance(actions[0], exp.ColumnDef):
    2518            actions = self.add_column_sql(expression)
    2519        elif isinstance(actions[0], exp.Schema):
    2520            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2521        elif isinstance(actions[0], exp.Delete):
    2522            actions = self.expressions(expression, key="actions", flat=True)
    2523        else:
    2524            actions = self.expressions(expression, key="actions", flat=True)
    2525
    2526        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2527        only = " ONLY" if expression.args.get("only") else ""
    2528        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"

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

    2530    def add_column_sql(self, expression: exp.AlterTable) -> str:
    2531        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    2532            return self.expressions(
    2533                expression,
    2534                key="actions",
    2535                prefix="ADD COLUMN ",
    2536            )
    2537        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    2539    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2540        expressions = self.expressions(expression)
    2541        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2542        return f"DROP{exists}{expressions}"

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

    2544    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2545        this = self.sql(expression, "this")
    2546        expression_ = self.sql(expression, "expression")
    2547        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2548
    2549        enforced = expression.args.get("enforced")
    2550        if enforced is not None:
    2551            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2552
    2553        return f"{add_constraint} {expression_}"

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

    2555    def distinct_sql(self, expression: exp.Distinct) -> str:
    2556        this = self.expressions(expression, flat=True)
    2557        this = f" {this}" if this else ""
    2558
    2559        on = self.sql(expression, "on")
    2560        on = f" ON {on}" if on else ""
    2561        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2569    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2570        return self.sql(
    2571            exp.Cast(
    2572                this=exp.Div(this=expression.this, expression=expression.expression),
    2573                to=exp.DataType(this=exp.DataType.Type.INT),
    2574            )
    2575        )

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

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

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

    2582    def div_sql(self, expression: exp.Div) -> str:
    2583        l, r = expression.left, expression.right
    2584
    2585        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    2586            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    2587
    2588        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    2589            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
    2590                *exp.DataType.FLOAT_TYPES
    2591            ):
    2592                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    2593
    2594        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    2595            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    2596                return self.sql(
    2597                    exp.cast(
    2598                        l / r,
    2599                        to=exp.DataType.Type.BIGINT,
    2600                    )
    2601                )
    2602
    2603        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2638    def is_sql(self, expression: exp.Is) -> str:
    2639        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    2640            return self.sql(
    2641                expression.this if expression.expression.this else exp.not_(expression.this)
    2642            )
    2643        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2687    def log_sql(self, expression: exp.Log) -> str:
    2688        this = expression.this
    2689        expr = expression.expression
    2690
    2691        if not self.dialect.LOG_BASE_FIRST:
    2692            this, expr = expr, this
    2693
    2694        return self.func("LOG", this, expr)

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

    2696    def use_sql(self, expression: exp.Use) -> str:
    2697        kind = self.sql(expression, "kind")
    2698        kind = f" {kind}" if kind else ""
    2699        this = self.sql(expression, "this")
    2700        this = f" {this}" if this else ""
    2701        return f"USE{kind}{this}"

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

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

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

    2707    def function_fallback_sql(self, expression: exp.Func) -> str:
    2708        args = []
    2709
    2710        for key in expression.arg_types:
    2711            arg_value = expression.args.get(key)
    2712
    2713            if isinstance(arg_value, list):
    2714                for value in arg_value:
    2715                    args.append(value)
    2716            elif arg_value is not None:
    2717                args.append(arg_value)
    2718
    2719        if self.normalize_functions:
    2720            name = expression.sql_name()
    2721        else:
    2722            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    2723
    2724        return self.func(name, *args)

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

    2726    def func(
    2727        self,
    2728        name: str,
    2729        *args: t.Optional[exp.Expression | str],
    2730        prefix: str = "(",
    2731        suffix: str = ")",
    2732    ) -> str:
    2733        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

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

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

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

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

    2744    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2745        return format_time(
    2746            self.sql(expression, "format"),
    2747            self.dialect.INVERSE_TIME_MAPPING,
    2748            self.dialect.INVERSE_TIME_TRIE,
    2749        )

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

    2751    def expressions(
    2752        self,
    2753        expression: t.Optional[exp.Expression] = None,
    2754        key: t.Optional[str] = None,
    2755        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    2756        flat: bool = False,
    2757        indent: bool = True,
    2758        skip_first: bool = False,
    2759        sep: str = ", ",
    2760        prefix: str = "",
    2761    ) -> str:
    2762        expressions = expression.args.get(key or "expressions") if expression else sqls
    2763
    2764        if not expressions:
    2765            return ""
    2766
    2767        if flat:
    2768            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    2769
    2770        num_sqls = len(expressions)
    2771
    2772        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2773        pad = " " * self.pad
    2774        stripped_sep = sep.strip()
    2775
    2776        result_sqls = []
    2777        for i, e in enumerate(expressions):
    2778            sql = self.sql(e, comment=False)
    2779            if not sql:
    2780                continue
    2781
    2782            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2783
    2784            if self.pretty:
    2785                if self.leading_comma:
    2786                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2787                else:
    2788                    result_sqls.append(
    2789                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2790                    )
    2791            else:
    2792                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2793
    2794        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2795        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

    2797    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2798        flat = flat or isinstance(expression.parent, exp.Properties)
    2799        expressions_sql = self.expressions(expression, flat=flat)
    2800        if flat:
    2801            return f"{op} {expressions_sql}"
    2802        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    2804    def naked_property(self, expression: exp.Property) -> str:
    2805        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2806        if not property_name:
    2807            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2808        return f"{property_name} {self.sql(expression, 'this')}"

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

    2810    def set_operation(self, expression: exp.Union, op: str) -> str:
    2811        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
    2812        op = self.seg(op)
    2813        return self.query_modifiers(
    2814            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2815        )

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

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

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

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

    2823    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2824        this = self.sql(expression, "this")
    2825        expressions = self.no_identify(self.expressions, expression)
    2826        expressions = (
    2827            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2828        )
    2829        return f"{this}{expressions}"

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

    2831    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2832        this = self.sql(expression, "this")
    2833        expressions = self.expressions(expression, flat=True)
    2834        return f"{this}({expressions})"

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

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

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

    2839    def when_sql(self, expression: exp.When) -> str:
    2840        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2841        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2842        condition = self.sql(expression, "condition")
    2843        condition = f" AND {condition}" if condition else ""
    2844
    2845        then_expression = expression.args.get("then")
    2846        if isinstance(then_expression, exp.Insert):
    2847            then = f"INSERT {self.sql(then_expression, 'this')}"
    2848            if "expression" in then_expression.args:
    2849                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2850        elif isinstance(then_expression, exp.Update):
    2851            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2852                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2853            else:
    2854                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2855        else:
    2856            then = self.sql(then_expression)
    2857        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    2859    def merge_sql(self, expression: exp.Merge) -> str:
    2860        table = expression.this
    2861        table_alias = ""
    2862
    2863        hints = table.args.get("hints")
    2864        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    2865            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    2866            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    2867
    2868        this = self.sql(table)
    2869        using = f"USING {self.sql(expression, 'using')}"
    2870        on = f"ON {self.sql(expression, 'on')}"
    2871        expressions = self.expressions(expression, sep=" ")
    2872
    2873        return self.prepend_ctes(
    2874            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
    2875        )

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

    2877    def tochar_sql(self, expression: exp.ToChar) -> str:
    2878        if expression.args.get("format"):
    2879            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2880
    2881        return self.sql(exp.cast(expression.this, "text"))

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

    2883    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2884        this = self.sql(expression, "this")
    2885        kind = self.sql(expression, "kind")
    2886        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2887        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2888        return f"{this}({kind}{args})"

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

    2890    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2891        this = self.sql(expression, "this")
    2892        max = self.sql(expression, "max")
    2893        min = self.sql(expression, "min")
    2894        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    2902    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    2903        expressions = self.expressions(expression, key="expressions", flat=True)
    2904        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    2905        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    2906        buckets = self.sql(expression, "buckets")
    2907        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    2909    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    2910        this = self.sql(expression, "this")
    2911        having = self.sql(expression, "having")
    2912
    2913        if having:
    2914            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    2915
    2916        return self.func("ANY_VALUE", this)

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

    2918    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    2919        transform = self.func("TRANSFORM", *expression.expressions)
    2920        row_format_before = self.sql(expression, "row_format_before")
    2921        row_format_before = f" {row_format_before}" if row_format_before else ""
    2922        record_writer = self.sql(expression, "record_writer")
    2923        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    2924        using = f" USING {self.sql(expression, 'command_script')}"
    2925        schema = self.sql(expression, "schema")
    2926        schema = f" AS {schema}" if schema else ""
    2927        row_format_after = self.sql(expression, "row_format_after")
    2928        row_format_after = f" {row_format_after}" if row_format_after else ""
    2929        record_reader = self.sql(expression, "record_reader")
    2930        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    2931        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

    2933    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    2934        key_block_size = self.sql(expression, "key_block_size")
    2935        if key_block_size:
    2936            return f"KEY_BLOCK_SIZE = {key_block_size}"
    2937
    2938        using = self.sql(expression, "using")
    2939        if using:
    2940            return f"USING {using}"
    2941
    2942        parser = self.sql(expression, "parser")
    2943        if parser:
    2944            return f"WITH PARSER {parser}"
    2945
    2946        comment = self.sql(expression, "comment")
    2947        if comment:
    2948            return f"COMMENT {comment}"
    2949
    2950        visible = expression.args.get("visible")
    2951        if visible is not None:
    2952            return "VISIBLE" if visible else "INVISIBLE"
    2953
    2954        engine_attr = self.sql(expression, "engine_attr")
    2955        if engine_attr:
    2956            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    2957
    2958        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    2959        if secondary_engine_attr:
    2960            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    2961
    2962        self.unsupported("Unsupported index constraint option.")
    2963        return ""

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

    2965    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    2966        kind = self.sql(expression, "kind")
    2967        kind = f"{kind} INDEX" if kind else "INDEX"
    2968        this = self.sql(expression, "this")
    2969        this = f" {this}" if this else ""
    2970        index_type = self.sql(expression, "index_type")
    2971        index_type = f" USING {index_type}" if index_type else ""
    2972        schema = self.sql(expression, "schema")
    2973        schema = f" {schema}" if schema else ""
    2974        options = self.expressions(expression, key="options", sep=" ")
    2975        options = f" {options}" if options else ""
    2976        return f"{kind}{this}{index_type}{schema}{options}"

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

    2978    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    2979        if self.NVL2_SUPPORTED:
    2980            return self.function_fallback_sql(expression)
    2981
    2982        case = exp.Case().when(
    2983            expression.this.is_(exp.null()).not_(copy=False),
    2984            expression.args["true"],
    2985            copy=False,
    2986        )
    2987        else_cond = expression.args.get("false")
    2988        if else_cond:
    2989            case.else_(else_cond, copy=False)
    2990
    2991        return self.sql(case)

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

    2993    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    2994        this = self.sql(expression, "this")
    2995        expr = self.sql(expression, "expression")
    2996        iterator = self.sql(expression, "iterator")
    2997        condition = self.sql(expression, "condition")
    2998        condition = f" IF {condition}" if condition else ""
    2999        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3007    def predict_sql(self, expression: exp.Predict) -> str:
    3008        model = self.sql(expression, "this")
    3009        model = f"MODEL {model}"
    3010        table = self.sql(expression, "expression")
    3011        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3012        parameters = self.sql(expression, "params_struct")
    3013        return self.func("PREDICT", model, table, parameters or None)

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

    3015    def forin_sql(self, expression: exp.ForIn) -> str:
    3016        this = self.sql(expression, "this")
    3017        expression_sql = self.sql(expression, "expression")
    3018        return f"FOR {this} DO {expression_sql}"

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

    3020    def refresh_sql(self, expression: exp.Refresh) -> str:
    3021        this = self.sql(expression, "this")
    3022        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3023        return f"REFRESH {table}{this}"

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

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