sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

       1from __future__ import annotations
       2
       3import logging
       4import typing as t
       5
       6from sqlglot import exp
       7from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
       8from sqlglot.helper import apply_index_offset, csv, seq_get, should_identify
       9from sqlglot.time import format_time
      10from sqlglot.tokens import TokenType
      11
      12logger = logging.getLogger("sqlglot")
      13
      14
      15class Generator:
      16    """
      17    Generator interprets the given syntax tree and produces a SQL string as an output.
      18
      19    Args:
      20        time_mapping (dict): the dictionary of custom time mappings in which the key
      21            represents a python time format and the output the target time format
      22        time_trie (trie): a trie of the time_mapping keys
      23        pretty (bool): if set to True the returned string will be formatted. Default: False.
      24        quote_start (str): specifies which starting character to use to delimit quotes. Default: '.
      25        quote_end (str): specifies which ending character to use to delimit quotes. Default: '.
      26        identifier_start (str): specifies which starting character to use to delimit identifiers. Default: ".
      27        identifier_end (str): specifies which ending character to use to delimit identifiers. Default: ".
      28        bit_start (str): specifies which starting character to use to delimit bit literals. Default: None.
      29        bit_end (str): specifies which ending character to use to delimit bit literals. Default: None.
      30        hex_start (str): specifies which starting character to use to delimit hex literals. Default: None.
      31        hex_end (str): specifies which ending character to use to delimit hex literals. Default: None.
      32        byte_start (str): specifies which starting character to use to delimit byte literals. Default: None.
      33        byte_end (str): specifies which ending character to use to delimit byte literals. Default: None.
      34        raw_start (str): specifies which starting character to use to delimit raw literals. Default: None.
      35        raw_end (str): specifies which ending character to use to delimit raw literals. Default: None.
      36        identify (bool | str): 'always': always quote, 'safe': quote identifiers if they don't contain an upcase, True defaults to always.
      37        normalize (bool): if set to True all identifiers will lower cased
      38        string_escape (str): specifies a string escape character. Default: '.
      39        identifier_escape (str): specifies an identifier escape character. Default: ".
      40        pad (int): determines padding in a formatted string. Default: 2.
      41        indent (int): determines the size of indentation in a formatted string. Default: 4.
      42        unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
      43        normalize_functions (str): normalize function names, "upper", "lower", or None
      44            Default: "upper"
      45        alias_post_tablesample (bool): if the table alias comes after tablesample
      46            Default: False
      47        identifiers_can_start_with_digit (bool): if an unquoted identifier can start with digit
      48            Default: False
      49        unsupported_level (ErrorLevel): determines the generator's behavior when it encounters
      50            unsupported expressions. Default ErrorLevel.WARN.
      51        null_ordering (str): Indicates the default null ordering method to use if not explicitly set.
      52            Options are "nulls_are_small", "nulls_are_large", "nulls_are_last".
      53            Default: "nulls_are_small"
      54        max_unsupported (int): Maximum number of unsupported messages to include in a raised UnsupportedError.
      55            This is only relevant if unsupported_level is ErrorLevel.RAISE.
      56            Default: 3
      57        leading_comma (bool): if the the comma is leading or trailing in select statements
      58            Default: False
      59        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
      60            The default is on the smaller end because the length only represents a segment and not the true
      61            line length.
      62            Default: 80
      63        comments: Whether or not to preserve comments in the output SQL code.
      64            Default: True
      65    """
      66
      67    TRANSFORMS = {
      68        exp.DateAdd: lambda self, e: self.func(
      69            "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      70        ),
      71        exp.TsOrDsAdd: lambda self, e: self.func(
      72            "TS_OR_DS_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      73        ),
      74        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
      75        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
      76        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
      77        exp.ExternalProperty: lambda self, e: "EXTERNAL",
      78        exp.LanguageProperty: lambda self, e: self.naked_property(e),
      79        exp.LocationProperty: lambda self, e: self.naked_property(e),
      80        exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
      81        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
      82        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
      83        exp.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
      84        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
      85        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
      86        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
      87        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
      88        exp.TemporaryProperty: lambda self, e: f"TEMPORARY",
      89        exp.TransientProperty: lambda self, e: "TRANSIENT",
      90        exp.StabilityProperty: lambda self, e: e.name,
      91        exp.VolatileProperty: lambda self, e: "VOLATILE",
      92        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
      93        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
      94        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
      95        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
      96        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
      97        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
      98        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
      99        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
     100        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
     101        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
     102        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
     103        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
     104        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
     105        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
     106    }
     107
     108    # Whether or not null ordering is supported in order by
     109    NULL_ORDERING_SUPPORTED = True
     110
     111    # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     112    LOCKING_READS_SUPPORTED = False
     113
     114    # Always do union distinct or union all
     115    EXPLICIT_UNION = False
     116
     117    # Wrap derived values in parens, usually standard but spark doesn't support it
     118    WRAP_DERIVED_VALUES = True
     119
     120    # Whether or not create function uses an AS before the RETURN
     121    CREATE_FUNCTION_RETURN_AS = True
     122
     123    # Whether or not MERGE ... WHEN MATCHED BY SOURCE is allowed
     124    MATCHED_BY_SOURCE = True
     125
     126    # Whether or not the INTERVAL expression works only with values like '1 day'
     127    SINGLE_STRING_INTERVAL = False
     128
     129    # Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     130    INTERVAL_ALLOWS_PLURAL_FORM = True
     131
     132    # Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI
     133    TABLESAMPLE_WITH_METHOD = True
     134
     135    # Whether or not to treat the number in TABLESAMPLE (50) as a percentage
     136    TABLESAMPLE_SIZE_IS_PERCENT = False
     137
     138    # Whether or not limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     139    LIMIT_FETCH = "ALL"
     140
     141    # Whether a table is allowed to be renamed with a db
     142    RENAME_TABLE_WITH_DB = True
     143
     144    # The separator for grouping sets and rollups
     145    GROUPINGS_SEP = ","
     146
     147    # The string used for creating index on a table
     148    INDEX_ON = "ON"
     149
     150    TYPE_MAPPING = {
     151        exp.DataType.Type.NCHAR: "CHAR",
     152        exp.DataType.Type.NVARCHAR: "VARCHAR",
     153        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     154        exp.DataType.Type.LONGTEXT: "TEXT",
     155        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     156        exp.DataType.Type.LONGBLOB: "BLOB",
     157        exp.DataType.Type.INET: "INET",
     158    }
     159
     160    STAR_MAPPING = {
     161        "except": "EXCEPT",
     162        "replace": "REPLACE",
     163    }
     164
     165    TIME_PART_SINGULARS = {
     166        "microseconds": "microsecond",
     167        "seconds": "second",
     168        "minutes": "minute",
     169        "hours": "hour",
     170        "days": "day",
     171        "weeks": "week",
     172        "months": "month",
     173        "quarters": "quarter",
     174        "years": "year",
     175    }
     176
     177    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     178
     179    STRUCT_DELIMITER = ("<", ">")
     180
     181    PARAMETER_TOKEN = "@"
     182
     183    PROPERTIES_LOCATION = {
     184        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     185        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     186        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     187        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     188        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     189        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     190        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     191        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     192        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     193        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     194        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     195        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     196        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     197        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     198        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     199        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     200        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     201        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     202        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     203        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     204        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     205        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     206        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     207        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     208        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     209        exp.LogProperty: exp.Properties.Location.POST_NAME,
     210        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     211        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     212        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     213        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     214        exp.Order: exp.Properties.Location.POST_SCHEMA,
     215        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     216        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     217        exp.Property: exp.Properties.Location.POST_WITH,
     218        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     219        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     220        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     221        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     222        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     223        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     224        exp.Set: exp.Properties.Location.POST_SCHEMA,
     225        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     226        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     227        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     228        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     229        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     230        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     231        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     232        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     233        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     234        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     235        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     236    }
     237
     238    JOIN_HINTS = True
     239    TABLE_HINTS = True
     240
     241    RESERVED_KEYWORDS: t.Set[str] = set()
     242    WITH_SEPARATED_COMMENTS = (exp.Select, exp.From, exp.Where, exp.With)
     243    UNWRAPPED_INTERVAL_VALUES = (exp.Column, exp.Literal, exp.Neg, exp.Paren)
     244
     245    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     246
     247    __slots__ = (
     248        "time_mapping",
     249        "time_trie",
     250        "pretty",
     251        "quote_start",
     252        "quote_end",
     253        "identifier_start",
     254        "identifier_end",
     255        "bit_start",
     256        "bit_end",
     257        "hex_start",
     258        "hex_end",
     259        "byte_start",
     260        "byte_end",
     261        "raw_start",
     262        "raw_end",
     263        "identify",
     264        "normalize",
     265        "string_escape",
     266        "identifier_escape",
     267        "pad",
     268        "index_offset",
     269        "unnest_column_only",
     270        "alias_post_tablesample",
     271        "identifiers_can_start_with_digit",
     272        "normalize_functions",
     273        "unsupported_level",
     274        "unsupported_messages",
     275        "null_ordering",
     276        "max_unsupported",
     277        "_indent",
     278        "_escaped_quote_end",
     279        "_escaped_identifier_end",
     280        "_leading_comma",
     281        "_max_text_width",
     282        "_comments",
     283        "_cache",
     284    )
     285
     286    def __init__(
     287        self,
     288        time_mapping=None,
     289        time_trie=None,
     290        pretty=None,
     291        quote_start=None,
     292        quote_end=None,
     293        identifier_start=None,
     294        identifier_end=None,
     295        bit_start=None,
     296        bit_end=None,
     297        hex_start=None,
     298        hex_end=None,
     299        byte_start=None,
     300        byte_end=None,
     301        raw_start=None,
     302        raw_end=None,
     303        identify=False,
     304        normalize=False,
     305        string_escape=None,
     306        identifier_escape=None,
     307        pad=2,
     308        indent=2,
     309        index_offset=0,
     310        unnest_column_only=False,
     311        alias_post_tablesample=False,
     312        identifiers_can_start_with_digit=False,
     313        normalize_functions="upper",
     314        unsupported_level=ErrorLevel.WARN,
     315        null_ordering=None,
     316        max_unsupported=3,
     317        leading_comma=False,
     318        max_text_width=80,
     319        comments=True,
     320    ):
     321        import sqlglot
     322
     323        self.time_mapping = time_mapping or {}
     324        self.time_trie = time_trie
     325        self.pretty = pretty if pretty is not None else sqlglot.pretty
     326        self.quote_start = quote_start or "'"
     327        self.quote_end = quote_end or "'"
     328        self.identifier_start = identifier_start or '"'
     329        self.identifier_end = identifier_end or '"'
     330        self.bit_start = bit_start
     331        self.bit_end = bit_end
     332        self.hex_start = hex_start
     333        self.hex_end = hex_end
     334        self.byte_start = byte_start
     335        self.byte_end = byte_end
     336        self.raw_start = raw_start
     337        self.raw_end = raw_end
     338        self.identify = identify
     339        self.normalize = normalize
     340        self.string_escape = string_escape or "'"
     341        self.identifier_escape = identifier_escape or '"'
     342        self.pad = pad
     343        self.index_offset = index_offset
     344        self.unnest_column_only = unnest_column_only
     345        self.alias_post_tablesample = alias_post_tablesample
     346        self.identifiers_can_start_with_digit = identifiers_can_start_with_digit
     347        self.normalize_functions = normalize_functions
     348        self.unsupported_level = unsupported_level
     349        self.unsupported_messages = []
     350        self.max_unsupported = max_unsupported
     351        self.null_ordering = null_ordering
     352        self._indent = indent
     353        self._escaped_quote_end = self.string_escape + self.quote_end
     354        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
     355        self._leading_comma = leading_comma
     356        self._max_text_width = max_text_width
     357        self._comments = comments
     358        self._cache = None
     359
     360    def generate(
     361        self,
     362        expression: t.Optional[exp.Expression],
     363        cache: t.Optional[t.Dict[int, str]] = None,
     364    ) -> str:
     365        """
     366        Generates a SQL string by interpreting the given syntax tree.
     367
     368        Args
     369            expression: the syntax tree.
     370            cache: an optional sql string cache. this leverages the hash of an expression which is slow, so only use this if you set _hash on each node.
     371
     372        Returns
     373            the SQL string.
     374        """
     375        if cache is not None:
     376            self._cache = cache
     377        self.unsupported_messages = []
     378        sql = self.sql(expression).strip()
     379        self._cache = None
     380
     381        if self.unsupported_level == ErrorLevel.IGNORE:
     382            return sql
     383
     384        if self.unsupported_level == ErrorLevel.WARN:
     385            for msg in self.unsupported_messages:
     386                logger.warning(msg)
     387        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     388            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     389
     390        if self.pretty:
     391            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     392        return sql
     393
     394    def unsupported(self, message: str) -> None:
     395        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     396            raise UnsupportedError(message)
     397        self.unsupported_messages.append(message)
     398
     399    def sep(self, sep: str = " ") -> str:
     400        return f"{sep.strip()}\n" if self.pretty else sep
     401
     402    def seg(self, sql: str, sep: str = " ") -> str:
     403        return f"{self.sep(sep)}{sql}"
     404
     405    def pad_comment(self, comment: str) -> str:
     406        comment = " " + comment if comment[0].strip() else comment
     407        comment = comment + " " if comment[-1].strip() else comment
     408        return comment
     409
     410    def maybe_comment(
     411        self,
     412        sql: str,
     413        expression: t.Optional[exp.Expression] = None,
     414        comments: t.Optional[t.List[str]] = None,
     415    ) -> str:
     416        comments = ((expression and expression.comments) if comments is None else comments) if self._comments else None  # type: ignore
     417
     418        if not comments or isinstance(expression, exp.Binary):
     419            return sql
     420
     421        sep = "\n" if self.pretty else " "
     422        comments_sql = sep.join(
     423            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     424        )
     425
     426        if not comments_sql:
     427            return sql
     428
     429        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     430            return (
     431                f"{self.sep()}{comments_sql}{sql}"
     432                if sql[0].isspace()
     433                else f"{comments_sql}{self.sep()}{sql}"
     434            )
     435
     436        return f"{sql} {comments_sql}"
     437
     438    def wrap(self, expression: exp.Expression | str) -> str:
     439        this_sql = self.indent(
     440            self.sql(expression)
     441            if isinstance(expression, (exp.Select, exp.Union))
     442            else self.sql(expression, "this"),
     443            level=1,
     444            pad=0,
     445        )
     446        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     447
     448    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     449        original = self.identify
     450        self.identify = False
     451        result = func(*args, **kwargs)
     452        self.identify = original
     453        return result
     454
     455    def normalize_func(self, name: str) -> str:
     456        if self.normalize_functions == "upper":
     457            return name.upper()
     458        if self.normalize_functions == "lower":
     459            return name.lower()
     460        return name
     461
     462    def indent(
     463        self,
     464        sql: str,
     465        level: int = 0,
     466        pad: t.Optional[int] = None,
     467        skip_first: bool = False,
     468        skip_last: bool = False,
     469    ) -> str:
     470        if not self.pretty:
     471            return sql
     472
     473        pad = self.pad if pad is None else pad
     474        lines = sql.split("\n")
     475
     476        return "\n".join(
     477            line
     478            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     479            else f"{' ' * (level * self._indent + pad)}{line}"
     480            for i, line in enumerate(lines)
     481        )
     482
     483    def sql(
     484        self,
     485        expression: t.Optional[str | exp.Expression],
     486        key: t.Optional[str] = None,
     487        comment: bool = True,
     488    ) -> str:
     489        if not expression:
     490            return ""
     491
     492        if isinstance(expression, str):
     493            return expression
     494
     495        if key:
     496            return self.sql(expression.args.get(key))
     497
     498        if self._cache is not None:
     499            expression_id = hash(expression)
     500
     501            if expression_id in self._cache:
     502                return self._cache[expression_id]
     503
     504        transform = self.TRANSFORMS.get(expression.__class__)
     505
     506        if callable(transform):
     507            sql = transform(self, expression)
     508        elif transform:
     509            sql = transform
     510        elif isinstance(expression, exp.Expression):
     511            exp_handler_name = f"{expression.key}_sql"
     512
     513            if hasattr(self, exp_handler_name):
     514                sql = getattr(self, exp_handler_name)(expression)
     515            elif isinstance(expression, exp.Func):
     516                sql = self.function_fallback_sql(expression)
     517            elif isinstance(expression, exp.Property):
     518                sql = self.property_sql(expression)
     519            else:
     520                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     521        else:
     522            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     523
     524        sql = self.maybe_comment(sql, expression) if self._comments and comment else sql
     525
     526        if self._cache is not None:
     527            self._cache[expression_id] = sql
     528        return sql
     529
     530    def uncache_sql(self, expression: exp.Uncache) -> str:
     531        table = self.sql(expression, "this")
     532        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     533        return f"UNCACHE TABLE{exists_sql} {table}"
     534
     535    def cache_sql(self, expression: exp.Cache) -> str:
     536        lazy = " LAZY" if expression.args.get("lazy") else ""
     537        table = self.sql(expression, "this")
     538        options = expression.args.get("options")
     539        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     540        sql = self.sql(expression, "expression")
     541        sql = f" AS{self.sep()}{sql}" if sql else ""
     542        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     543        return self.prepend_ctes(expression, sql)
     544
     545    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     546        if isinstance(expression.parent, exp.Cast):
     547            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     548        default = "DEFAULT " if expression.args.get("default") else ""
     549        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     550
     551    def column_sql(self, expression: exp.Column) -> str:
     552        return ".".join(
     553            self.sql(part)
     554            for part in (
     555                expression.args.get("catalog"),
     556                expression.args.get("db"),
     557                expression.args.get("table"),
     558                expression.args.get("this"),
     559            )
     560            if part
     561        )
     562
     563    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     564        this = self.sql(expression, "this")
     565        this = f" {this}" if this else ""
     566        position = self.sql(expression, "position")
     567        return f"{position}{this}"
     568
     569    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     570        column = self.sql(expression, "this")
     571        kind = self.sql(expression, "kind")
     572        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     573        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     574        kind = f"{sep}{kind}" if kind else ""
     575        constraints = f" {constraints}" if constraints else ""
     576        position = self.sql(expression, "position")
     577        position = f" {position}" if position else ""
     578
     579        return f"{exists}{column}{kind}{constraints}{position}"
     580
     581    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     582        this = self.sql(expression, "this")
     583        kind_sql = self.sql(expression, "kind").strip()
     584        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     585
     586    def autoincrementcolumnconstraint_sql(self, _) -> str:
     587        return self.token_sql(TokenType.AUTO_INCREMENT)
     588
     589    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     590        if isinstance(expression.this, list):
     591            this = self.wrap(self.expressions(expression, key="this", flat=True))
     592        else:
     593            this = self.sql(expression, "this")
     594
     595        return f"COMPRESS {this}"
     596
     597    def generatedasidentitycolumnconstraint_sql(
     598        self, expression: exp.GeneratedAsIdentityColumnConstraint
     599    ) -> str:
     600        this = ""
     601        if expression.this is not None:
     602            on_null = "ON NULL " if expression.args.get("on_null") else ""
     603            this = " ALWAYS " if expression.this else f" BY DEFAULT {on_null}"
     604
     605        start = expression.args.get("start")
     606        start = f"START WITH {start}" if start else ""
     607        increment = expression.args.get("increment")
     608        increment = f" INCREMENT BY {increment}" if increment else ""
     609        minvalue = expression.args.get("minvalue")
     610        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
     611        maxvalue = expression.args.get("maxvalue")
     612        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
     613        cycle = expression.args.get("cycle")
     614        cycle_sql = ""
     615
     616        if cycle is not None:
     617            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
     618            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
     619
     620        sequence_opts = ""
     621        if start or increment or cycle_sql:
     622            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
     623            sequence_opts = f" ({sequence_opts.strip()})"
     624
     625        expr = self.sql(expression, "expression")
     626        expr = f"({expr})" if expr else "IDENTITY"
     627
     628        return f"GENERATED{this}AS {expr}{sequence_opts}"
     629
     630    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
     631        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
     632
     633    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
     634        desc = expression.args.get("desc")
     635        if desc is not None:
     636            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
     637        return f"PRIMARY KEY"
     638
     639    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
     640        this = self.sql(expression, "this")
     641        this = f" {this}" if this else ""
     642        return f"UNIQUE{this}"
     643
     644    def create_sql(self, expression: exp.Create) -> str:
     645        kind = self.sql(expression, "kind").upper()
     646        properties = expression.args.get("properties")
     647        properties_exp = expression.copy()
     648        properties_locs = self.locate_properties(properties) if properties else {}
     649        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
     650            exp.Properties.Location.POST_WITH
     651        ):
     652            properties_exp.set(
     653                "properties",
     654                exp.Properties(
     655                    expressions=[
     656                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
     657                        *properties_locs[exp.Properties.Location.POST_WITH],
     658                    ]
     659                ),
     660            )
     661        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
     662            this_name = self.sql(expression.this, "this")
     663            this_properties = self.properties(
     664                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
     665                wrapped=False,
     666            )
     667            this_schema = f"({self.expressions(expression.this)})"
     668            this = f"{this_name}, {this_properties} {this_schema}"
     669            properties_sql = ""
     670        else:
     671            this = self.sql(expression, "this")
     672            properties_sql = self.sql(properties_exp, "properties")
     673        begin = " BEGIN" if expression.args.get("begin") else ""
     674        expression_sql = self.sql(expression, "expression")
     675        if expression_sql:
     676            expression_sql = f"{begin}{self.sep()}{expression_sql}"
     677
     678            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
     679                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
     680                    postalias_props_sql = self.properties(
     681                        exp.Properties(
     682                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
     683                        ),
     684                        wrapped=False,
     685                    )
     686                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
     687                else:
     688                    expression_sql = f" AS{expression_sql}"
     689
     690        postindex_props_sql = ""
     691        if properties_locs.get(exp.Properties.Location.POST_INDEX):
     692            postindex_props_sql = self.properties(
     693                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
     694                wrapped=False,
     695                prefix=" ",
     696            )
     697
     698        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
     699        indexes = f" {indexes}" if indexes else ""
     700        index_sql = indexes + postindex_props_sql
     701
     702        replace = " OR REPLACE" if expression.args.get("replace") else ""
     703        unique = " UNIQUE" if expression.args.get("unique") else ""
     704
     705        postcreate_props_sql = ""
     706        if properties_locs.get(exp.Properties.Location.POST_CREATE):
     707            postcreate_props_sql = self.properties(
     708                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
     709                sep=" ",
     710                prefix=" ",
     711                wrapped=False,
     712            )
     713
     714        modifiers = "".join((replace, unique, postcreate_props_sql))
     715
     716        postexpression_props_sql = ""
     717        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
     718            postexpression_props_sql = self.properties(
     719                exp.Properties(
     720                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
     721                ),
     722                sep=" ",
     723                prefix=" ",
     724                wrapped=False,
     725            )
     726
     727        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
     728        no_schema_binding = (
     729            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
     730        )
     731
     732        clone = self.sql(expression, "clone")
     733        clone = f" {clone}" if clone else ""
     734
     735        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
     736        return self.prepend_ctes(expression, expression_sql)
     737
     738    def clone_sql(self, expression: exp.Clone) -> str:
     739        this = self.sql(expression, "this")
     740        when = self.sql(expression, "when")
     741
     742        if when:
     743            kind = self.sql(expression, "kind")
     744            expr = self.sql(expression, "expression")
     745            return f"CLONE {this} {when} ({kind} => {expr})"
     746
     747        return f"CLONE {this}"
     748
     749    def describe_sql(self, expression: exp.Describe) -> str:
     750        return f"DESCRIBE {self.sql(expression, 'this')}"
     751
     752    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
     753        with_ = self.sql(expression, "with")
     754        if with_:
     755            sql = f"{with_}{self.sep()}{sql}"
     756        return sql
     757
     758    def with_sql(self, expression: exp.With) -> str:
     759        sql = self.expressions(expression, flat=True)
     760        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
     761
     762        return f"WITH {recursive}{sql}"
     763
     764    def cte_sql(self, expression: exp.CTE) -> str:
     765        alias = self.sql(expression, "alias")
     766        return f"{alias} AS {self.wrap(expression)}"
     767
     768    def tablealias_sql(self, expression: exp.TableAlias) -> str:
     769        alias = self.sql(expression, "this")
     770        columns = self.expressions(expression, key="columns", flat=True)
     771        columns = f"({columns})" if columns else ""
     772        return f"{alias}{columns}"
     773
     774    def bitstring_sql(self, expression: exp.BitString) -> str:
     775        this = self.sql(expression, "this")
     776        if self.bit_start:
     777            return f"{self.bit_start}{this}{self.bit_end}"
     778        return f"{int(this, 2)}"
     779
     780    def hexstring_sql(self, expression: exp.HexString) -> str:
     781        this = self.sql(expression, "this")
     782        if self.hex_start:
     783            return f"{self.hex_start}{this}{self.hex_end}"
     784        return f"{int(this, 16)}"
     785
     786    def bytestring_sql(self, expression: exp.ByteString) -> str:
     787        this = self.sql(expression, "this")
     788        if self.byte_start:
     789            return f"{self.byte_start}{this}{self.byte_end}"
     790        return this
     791
     792    def rawstring_sql(self, expression: exp.RawString) -> str:
     793        if self.raw_start:
     794            return f"{self.raw_start}{expression.name}{self.raw_end}"
     795        return self.sql(exp.Literal.string(expression.name.replace("\\", "\\\\")))
     796
     797    def datatypesize_sql(self, expression: exp.DataTypeSize) -> str:
     798        this = self.sql(expression, "this")
     799        specifier = self.sql(expression, "expression")
     800        specifier = f" {specifier}" if specifier else ""
     801        return f"{this}{specifier}"
     802
     803    def datatype_sql(self, expression: exp.DataType) -> str:
     804        type_value = expression.this
     805        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
     806        nested = ""
     807        interior = self.expressions(expression, flat=True)
     808        values = ""
     809        if interior:
     810            if expression.args.get("nested"):
     811                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
     812                if expression.args.get("values") is not None:
     813                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
     814                    values = self.expressions(expression, key="values", flat=True)
     815                    values = f"{delimiters[0]}{values}{delimiters[1]}"
     816            else:
     817                nested = f"({interior})"
     818
     819        return f"{type_sql}{nested}{values}"
     820
     821    def directory_sql(self, expression: exp.Directory) -> str:
     822        local = "LOCAL " if expression.args.get("local") else ""
     823        row_format = self.sql(expression, "row_format")
     824        row_format = f" {row_format}" if row_format else ""
     825        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
     826
     827    def delete_sql(self, expression: exp.Delete) -> str:
     828        this = self.sql(expression, "this")
     829        this = f" FROM {this}" if this else ""
     830        using_sql = (
     831            f" USING {self.expressions(expression, key='using', sep=', USING ')}"
     832            if expression.args.get("using")
     833            else ""
     834        )
     835        where_sql = self.sql(expression, "where")
     836        returning = self.sql(expression, "returning")
     837        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
     838        return self.prepend_ctes(expression, sql)
     839
     840    def drop_sql(self, expression: exp.Drop) -> str:
     841        this = self.sql(expression, "this")
     842        kind = expression.args["kind"]
     843        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
     844        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
     845        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
     846        cascade = " CASCADE" if expression.args.get("cascade") else ""
     847        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
     848        purge = " PURGE" if expression.args.get("purge") else ""
     849        return (
     850            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
     851        )
     852
     853    def except_sql(self, expression: exp.Except) -> str:
     854        return self.prepend_ctes(
     855            expression,
     856            self.set_operation(expression, self.except_op(expression)),
     857        )
     858
     859    def except_op(self, expression: exp.Except) -> str:
     860        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
     861
     862    def fetch_sql(self, expression: exp.Fetch) -> str:
     863        direction = expression.args.get("direction")
     864        direction = f" {direction.upper()}" if direction else ""
     865        count = expression.args.get("count")
     866        count = f" {count}" if count else ""
     867        if expression.args.get("percent"):
     868            count = f"{count} PERCENT"
     869        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
     870        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
     871
     872    def filter_sql(self, expression: exp.Filter) -> str:
     873        this = self.sql(expression, "this")
     874        where = self.sql(expression, "expression")[1:]  # where has a leading space
     875        return f"{this} FILTER({where})"
     876
     877    def hint_sql(self, expression: exp.Hint) -> str:
     878        if self.sql(expression, "this"):
     879            self.unsupported("Hints are not supported")
     880        return ""
     881
     882    def index_sql(self, expression: exp.Index) -> str:
     883        unique = "UNIQUE " if expression.args.get("unique") else ""
     884        primary = "PRIMARY " if expression.args.get("primary") else ""
     885        amp = "AMP " if expression.args.get("amp") else ""
     886        name = f"{expression.name} " if expression.name else ""
     887        table = self.sql(expression, "table")
     888        table = f"{self.INDEX_ON} {table} " if table else ""
     889        index = "INDEX " if not table else ""
     890        columns = self.expressions(expression, key="columns", flat=True)
     891        partition_by = self.expressions(expression, key="partition_by", flat=True)
     892        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
     893        return f"{unique}{primary}{amp}{index}{name}{table}({columns}){partition_by}"
     894
     895    def identifier_sql(self, expression: exp.Identifier) -> str:
     896        text = expression.name
     897        lower = text.lower()
     898        text = lower if self.normalize and not expression.quoted else text
     899        text = text.replace(self.identifier_end, self._escaped_identifier_end)
     900        if (
     901            expression.quoted
     902            or should_identify(text, self.identify)
     903            or lower in self.RESERVED_KEYWORDS
     904            or (not self.identifiers_can_start_with_digit and text[:1].isdigit())
     905        ):
     906            text = f"{self.identifier_start}{text}{self.identifier_end}"
     907        return text
     908
     909    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
     910        input_format = self.sql(expression, "input_format")
     911        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
     912        output_format = self.sql(expression, "output_format")
     913        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
     914        return self.sep().join((input_format, output_format))
     915
     916    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
     917        string = self.sql(exp.Literal.string(expression.name))
     918        return f"{prefix}{string}"
     919
     920    def partition_sql(self, expression: exp.Partition) -> str:
     921        return f"PARTITION({self.expressions(expression)})"
     922
     923    def properties_sql(self, expression: exp.Properties) -> str:
     924        root_properties = []
     925        with_properties = []
     926
     927        for p in expression.expressions:
     928            p_loc = self.PROPERTIES_LOCATION[p.__class__]
     929            if p_loc == exp.Properties.Location.POST_WITH:
     930                with_properties.append(p)
     931            elif p_loc == exp.Properties.Location.POST_SCHEMA:
     932                root_properties.append(p)
     933
     934        return self.root_properties(
     935            exp.Properties(expressions=root_properties)
     936        ) + self.with_properties(exp.Properties(expressions=with_properties))
     937
     938    def root_properties(self, properties: exp.Properties) -> str:
     939        if properties.expressions:
     940            return self.sep() + self.expressions(properties, indent=False, sep=" ")
     941        return ""
     942
     943    def properties(
     944        self,
     945        properties: exp.Properties,
     946        prefix: str = "",
     947        sep: str = ", ",
     948        suffix: str = "",
     949        wrapped: bool = True,
     950    ) -> str:
     951        if properties.expressions:
     952            expressions = self.expressions(properties, sep=sep, indent=False)
     953            expressions = self.wrap(expressions) if wrapped else expressions
     954            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
     955        return ""
     956
     957    def with_properties(self, properties: exp.Properties) -> str:
     958        return self.properties(properties, prefix=self.seg("WITH"))
     959
     960    def locate_properties(
     961        self, properties: exp.Properties
     962    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
     963        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
     964            key: [] for key in exp.Properties.Location
     965        }
     966
     967        for p in properties.expressions:
     968            p_loc = self.PROPERTIES_LOCATION[p.__class__]
     969            if p_loc == exp.Properties.Location.POST_NAME:
     970                properties_locs[exp.Properties.Location.POST_NAME].append(p)
     971            elif p_loc == exp.Properties.Location.POST_INDEX:
     972                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
     973            elif p_loc == exp.Properties.Location.POST_SCHEMA:
     974                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
     975            elif p_loc == exp.Properties.Location.POST_WITH:
     976                properties_locs[exp.Properties.Location.POST_WITH].append(p)
     977            elif p_loc == exp.Properties.Location.POST_CREATE:
     978                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
     979            elif p_loc == exp.Properties.Location.POST_ALIAS:
     980                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
     981            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
     982                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
     983            elif p_loc == exp.Properties.Location.UNSUPPORTED:
     984                self.unsupported(f"Unsupported property {p.key}")
     985
     986        return properties_locs
     987
     988    def property_sql(self, expression: exp.Property) -> str:
     989        property_cls = expression.__class__
     990        if property_cls == exp.Property:
     991            return f"{expression.name}={self.sql(expression, 'value')}"
     992
     993        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
     994        if not property_name:
     995            self.unsupported(f"Unsupported property {expression.key}")
     996
     997        return f"{property_name}={self.sql(expression, 'this')}"
     998
     999    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1000        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1001        options = f" {options}" if options else ""
    1002        return f"LIKE {self.sql(expression, 'this')}{options}"
    1003
    1004    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1005        no = "NO " if expression.args.get("no") else ""
    1006        protection = " PROTECTION" if expression.args.get("protection") else ""
    1007        return f"{no}FALLBACK{protection}"
    1008
    1009    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1010        no = "NO " if expression.args.get("no") else ""
    1011        local = expression.args.get("local")
    1012        local = f"{local} " if local else ""
    1013        dual = "DUAL " if expression.args.get("dual") else ""
    1014        before = "BEFORE " if expression.args.get("before") else ""
    1015        after = "AFTER " if expression.args.get("after") else ""
    1016        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1017
    1018    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1019        freespace = self.sql(expression, "this")
    1020        percent = " PERCENT" if expression.args.get("percent") else ""
    1021        return f"FREESPACE={freespace}{percent}"
    1022
    1023    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1024        if expression.args.get("default"):
    1025            property = "DEFAULT"
    1026        elif expression.args.get("on"):
    1027            property = "ON"
    1028        else:
    1029            property = "OFF"
    1030        return f"CHECKSUM={property}"
    1031
    1032    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1033        if expression.args.get("no"):
    1034            return "NO MERGEBLOCKRATIO"
    1035        if expression.args.get("default"):
    1036            return "DEFAULT MERGEBLOCKRATIO"
    1037
    1038        percent = " PERCENT" if expression.args.get("percent") else ""
    1039        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1040
    1041    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1042        default = expression.args.get("default")
    1043        minimum = expression.args.get("minimum")
    1044        maximum = expression.args.get("maximum")
    1045        if default or minimum or maximum:
    1046            if default:
    1047                prop = "DEFAULT"
    1048            elif minimum:
    1049                prop = "MINIMUM"
    1050            else:
    1051                prop = "MAXIMUM"
    1052            return f"{prop} DATABLOCKSIZE"
    1053        units = expression.args.get("units")
    1054        units = f" {units}" if units else ""
    1055        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1056
    1057    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1058        autotemp = expression.args.get("autotemp")
    1059        always = expression.args.get("always")
    1060        default = expression.args.get("default")
    1061        manual = expression.args.get("manual")
    1062        never = expression.args.get("never")
    1063
    1064        if autotemp is not None:
    1065            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1066        elif always:
    1067            prop = "ALWAYS"
    1068        elif default:
    1069            prop = "DEFAULT"
    1070        elif manual:
    1071            prop = "MANUAL"
    1072        elif never:
    1073            prop = "NEVER"
    1074        return f"BLOCKCOMPRESSION={prop}"
    1075
    1076    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1077        no = expression.args.get("no")
    1078        no = " NO" if no else ""
    1079        concurrent = expression.args.get("concurrent")
    1080        concurrent = " CONCURRENT" if concurrent else ""
    1081
    1082        for_ = ""
    1083        if expression.args.get("for_all"):
    1084            for_ = " FOR ALL"
    1085        elif expression.args.get("for_insert"):
    1086            for_ = " FOR INSERT"
    1087        elif expression.args.get("for_none"):
    1088            for_ = " FOR NONE"
    1089        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
    1090
    1091    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1092        kind = expression.args.get("kind")
    1093        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1094        for_or_in = expression.args.get("for_or_in")
    1095        lock_type = expression.args.get("lock_type")
    1096        override = " OVERRIDE" if expression.args.get("override") else ""
    1097        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
    1098
    1099    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1100        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1101        statistics = expression.args.get("statistics")
    1102        statistics_sql = ""
    1103        if statistics is not None:
    1104            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1105        return f"{data_sql}{statistics_sql}"
    1106
    1107    def insert_sql(self, expression: exp.Insert) -> str:
    1108        overwrite = expression.args.get("overwrite")
    1109
    1110        if isinstance(expression.this, exp.Directory):
    1111            this = "OVERWRITE " if overwrite else "INTO "
    1112        else:
    1113            this = "OVERWRITE TABLE " if overwrite else "INTO "
    1114
    1115        alternative = expression.args.get("alternative")
    1116        alternative = f" OR {alternative} " if alternative else " "
    1117        this = f"{this}{self.sql(expression, 'this')}"
    1118
    1119        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1120        partition_sql = (
    1121            self.sql(expression, "partition") if expression.args.get("partition") else ""
    1122        )
    1123        expression_sql = self.sql(expression, "expression")
    1124        conflict = self.sql(expression, "conflict")
    1125        returning = self.sql(expression, "returning")
    1126        sep = self.sep() if partition_sql else ""
    1127        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{conflict}{returning}"
    1128        return self.prepend_ctes(expression, sql)
    1129
    1130    def intersect_sql(self, expression: exp.Intersect) -> str:
    1131        return self.prepend_ctes(
    1132            expression,
    1133            self.set_operation(expression, self.intersect_op(expression)),
    1134        )
    1135
    1136    def intersect_op(self, expression: exp.Intersect) -> str:
    1137        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
    1138
    1139    def introducer_sql(self, expression: exp.Introducer) -> str:
    1140        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1141
    1142    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1143        return expression.name.upper()
    1144
    1145    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1146        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1147        constraint = self.sql(expression, "constraint")
    1148        if constraint:
    1149            constraint = f"ON CONSTRAINT {constraint}"
    1150        key = self.expressions(expression, key="key", flat=True)
    1151        do = "" if expression.args.get("duplicate") else " DO "
    1152        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1153        expressions = self.expressions(expression, flat=True)
    1154        if expressions:
    1155            expressions = f"UPDATE SET {expressions}"
    1156        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
    1157
    1158    def returning_sql(self, expression: exp.Returning) -> str:
    1159        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1160
    1161    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1162        fields = expression.args.get("fields")
    1163        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1164        escaped = expression.args.get("escaped")
    1165        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1166        items = expression.args.get("collection_items")
    1167        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1168        keys = expression.args.get("map_keys")
    1169        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1170        lines = expression.args.get("lines")
    1171        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1172        null = expression.args.get("null")
    1173        null = f" NULL DEFINED AS {null}" if null else ""
    1174        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1175
    1176    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1177        table = ".".join(
    1178            part
    1179            for part in [
    1180                self.sql(expression, "catalog"),
    1181                self.sql(expression, "db"),
    1182                self.sql(expression, "this"),
    1183            ]
    1184            if part
    1185        )
    1186
    1187        alias = self.sql(expression, "alias")
    1188        alias = f"{sep}{alias}" if alias else ""
    1189        hints = self.expressions(expression, key="hints", flat=True)
    1190        hints = f" WITH ({hints})" if hints and self.TABLE_HINTS else ""
    1191        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1192        pivots = f" {pivots}" if pivots else ""
    1193        joins = self.expressions(expression, key="joins", sep="")
    1194        laterals = self.expressions(expression, key="laterals", sep="")
    1195        system_time = expression.args.get("system_time")
    1196        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
    1197
    1198        return f"{table}{system_time}{alias}{hints}{pivots}{joins}{laterals}"
    1199
    1200    def tablesample_sql(
    1201        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1202    ) -> str:
    1203        if self.alias_post_tablesample and expression.this.alias:
    1204            table = expression.this.copy()
    1205            table.set("alias", None)
    1206            this = self.sql(table)
    1207            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1208        else:
    1209            this = self.sql(expression, "this")
    1210            alias = ""
    1211        method = self.sql(expression, "method")
    1212        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1213        numerator = self.sql(expression, "bucket_numerator")
    1214        denominator = self.sql(expression, "bucket_denominator")
    1215        field = self.sql(expression, "bucket_field")
    1216        field = f" ON {field}" if field else ""
    1217        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1218        percent = self.sql(expression, "percent")
    1219        percent = f"{percent} PERCENT" if percent else ""
    1220        rows = self.sql(expression, "rows")
    1221        rows = f"{rows} ROWS" if rows else ""
    1222        size = self.sql(expression, "size")
    1223        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1224            size = f"{size} PERCENT"
    1225        seed = self.sql(expression, "seed")
    1226        seed = f" {seed_prefix} ({seed})" if seed else ""
    1227        kind = expression.args.get("kind", "TABLESAMPLE")
    1228        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"
    1229
    1230    def pivot_sql(self, expression: exp.Pivot) -> str:
    1231        expressions = self.expressions(expression, flat=True)
    1232
    1233        if expression.this:
    1234            this = self.sql(expression, "this")
    1235            on = f"{self.seg('ON')} {expressions}"
    1236            using = self.expressions(expression, key="using", flat=True)
    1237            using = f"{self.seg('USING')} {using}" if using else ""
    1238            group = self.sql(expression, "group")
    1239            return f"PIVOT {this}{on}{using}{group}"
    1240
    1241        alias = self.sql(expression, "alias")
    1242        alias = f" AS {alias}" if alias else ""
    1243        unpivot = expression.args.get("unpivot")
    1244        direction = "UNPIVOT" if unpivot else "PIVOT"
    1245        field = self.sql(expression, "field")
    1246        return f"{direction}({expressions} FOR {field}){alias}"
    1247
    1248    def tuple_sql(self, expression: exp.Tuple) -> str:
    1249        return f"({self.expressions(expression, flat=True)})"
    1250
    1251    def update_sql(self, expression: exp.Update) -> str:
    1252        this = self.sql(expression, "this")
    1253        set_sql = self.expressions(expression, flat=True)
    1254        from_sql = self.sql(expression, "from")
    1255        where_sql = self.sql(expression, "where")
    1256        returning = self.sql(expression, "returning")
    1257        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
    1258        return self.prepend_ctes(expression, sql)
    1259
    1260    def values_sql(self, expression: exp.Values) -> str:
    1261        args = self.expressions(expression)
    1262        alias = self.sql(expression, "alias")
    1263        values = f"VALUES{self.seg('')}{args}"
    1264        values = (
    1265            f"({values})"
    1266            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1267            else values
    1268        )
    1269        return f"{values} AS {alias}" if alias else values
    1270
    1271    def var_sql(self, expression: exp.Var) -> str:
    1272        return self.sql(expression, "this")
    1273
    1274    def into_sql(self, expression: exp.Into) -> str:
    1275        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1276        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    1277        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    1278
    1279    def from_sql(self, expression: exp.From) -> str:
    1280        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    1281
    1282    def group_sql(self, expression: exp.Group) -> str:
    1283        group_by = self.op_expressions("GROUP BY", expression)
    1284        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1285        grouping_sets = (
    1286            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1287        )
    1288
    1289        cube = expression.args.get("cube", [])
    1290        if seq_get(cube, 0) is True:
    1291            return f"{group_by}{self.seg('WITH CUBE')}"
    1292        else:
    1293            cube_sql = self.expressions(expression, key="cube", indent=False)
    1294            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1295
    1296        rollup = expression.args.get("rollup", [])
    1297        if seq_get(rollup, 0) is True:
    1298            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1299        else:
    1300            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1301            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1302
    1303        groupings = csv(
    1304            grouping_sets,
    1305            cube_sql,
    1306            rollup_sql,
    1307            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1308            sep=self.GROUPINGS_SEP,
    1309        )
    1310
    1311        if expression.args.get("expressions") and groupings:
    1312            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1313
    1314        return f"{group_by}{groupings}"
    1315
    1316    def having_sql(self, expression: exp.Having) -> str:
    1317        this = self.indent(self.sql(expression, "this"))
    1318        return f"{self.seg('HAVING')}{self.sep()}{this}"
    1319
    1320    def join_sql(self, expression: exp.Join) -> str:
    1321        op_sql = " ".join(
    1322            op
    1323            for op in (
    1324                expression.method,
    1325                "GLOBAL" if expression.args.get("global") else None,
    1326                expression.side,
    1327                expression.kind,
    1328                expression.hint if self.JOIN_HINTS else None,
    1329            )
    1330            if op
    1331        )
    1332        on_sql = self.sql(expression, "on")
    1333        using = expression.args.get("using")
    1334
    1335        if not on_sql and using:
    1336            on_sql = csv(*(self.sql(column) for column in using))
    1337
    1338        this_sql = self.sql(expression, "this")
    1339
    1340        if on_sql:
    1341            on_sql = self.indent(on_sql, skip_first=True)
    1342            space = self.seg(" " * self.pad) if self.pretty else " "
    1343            if using:
    1344                on_sql = f"{space}USING ({on_sql})"
    1345            else:
    1346                on_sql = f"{space}ON {on_sql}"
    1347        elif not op_sql:
    1348            return f", {this_sql}"
    1349
    1350        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1351        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
    1352
    1353    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    1354        args = self.expressions(expression, flat=True)
    1355        args = f"({args})" if len(args.split(",")) > 1 else args
    1356        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    1357
    1358    def lateral_sql(self, expression: exp.Lateral) -> str:
    1359        this = self.sql(expression, "this")
    1360
    1361        if isinstance(expression.this, exp.Subquery):
    1362            return f"LATERAL {this}"
    1363
    1364        if expression.args.get("view"):
    1365            alias = expression.args["alias"]
    1366            columns = self.expressions(alias, key="columns", flat=True)
    1367            table = f" {alias.name}" if alias.name else ""
    1368            columns = f" AS {columns}" if columns else ""
    1369            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1370            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1371
    1372        alias = self.sql(expression, "alias")
    1373        alias = f" AS {alias}" if alias else ""
    1374        return f"LATERAL {this}{alias}"
    1375
    1376    def limit_sql(self, expression: exp.Limit) -> str:
    1377        this = self.sql(expression, "this")
    1378        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"
    1379
    1380    def offset_sql(self, expression: exp.Offset) -> str:
    1381        this = self.sql(expression, "this")
    1382        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
    1383
    1384    def setitem_sql(self, expression: exp.SetItem) -> str:
    1385        kind = self.sql(expression, "kind")
    1386        kind = f"{kind} " if kind else ""
    1387        this = self.sql(expression, "this")
    1388        expressions = self.expressions(expression)
    1389        collate = self.sql(expression, "collate")
    1390        collate = f" COLLATE {collate}" if collate else ""
    1391        global_ = "GLOBAL " if expression.args.get("global") else ""
    1392        return f"{global_}{kind}{this}{expressions}{collate}"
    1393
    1394    def set_sql(self, expression: exp.Set) -> str:
    1395        expressions = (
    1396            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1397        )
    1398        return f"SET{expressions}"
    1399
    1400    def pragma_sql(self, expression: exp.Pragma) -> str:
    1401        return f"PRAGMA {self.sql(expression, 'this')}"
    1402
    1403    def lock_sql(self, expression: exp.Lock) -> str:
    1404        if not self.LOCKING_READS_SUPPORTED:
    1405            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1406            return ""
    1407
    1408        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1409        expressions = self.expressions(expression, flat=True)
    1410        expressions = f" OF {expressions}" if expressions else ""
    1411        wait = expression.args.get("wait")
    1412
    1413        if wait is not None:
    1414            if isinstance(wait, exp.Literal):
    1415                wait = f" WAIT {self.sql(wait)}"
    1416            else:
    1417                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1418
    1419        return f"{lock_type}{expressions}{wait or ''}"
    1420
    1421    def literal_sql(self, expression: exp.Literal) -> str:
    1422        text = expression.this or ""
    1423        if expression.is_string:
    1424            text = text.replace(self.quote_end, self._escaped_quote_end)
    1425            if self.pretty:
    1426                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1427            text = f"{self.quote_start}{text}{self.quote_end}"
    1428        return text
    1429
    1430    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1431        local = " LOCAL" if expression.args.get("local") else ""
    1432        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1433        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1434        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1435        partition = self.sql(expression, "partition")
    1436        partition = f" {partition}" if partition else ""
    1437        input_format = self.sql(expression, "input_format")
    1438        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1439        serde = self.sql(expression, "serde")
    1440        serde = f" SERDE {serde}" if serde else ""
    1441        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    1442
    1443    def null_sql(self, *_) -> str:
    1444        return "NULL"
    1445
    1446    def boolean_sql(self, expression: exp.Boolean) -> str:
    1447        return "TRUE" if expression.this else "FALSE"
    1448
    1449    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1450        this = self.sql(expression, "this")
    1451        this = f"{this} " if this else this
    1452        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
    1453
    1454    def cluster_sql(self, expression: exp.Cluster) -> str:
    1455        return self.op_expressions("CLUSTER BY", expression)
    1456
    1457    def distribute_sql(self, expression: exp.Distribute) -> str:
    1458        return self.op_expressions("DISTRIBUTE BY", expression)
    1459
    1460    def sort_sql(self, expression: exp.Sort) -> str:
    1461        return self.op_expressions("SORT BY", expression)
    1462
    1463    def ordered_sql(self, expression: exp.Ordered) -> str:
    1464        desc = expression.args.get("desc")
    1465        asc = not desc
    1466
    1467        nulls_first = expression.args.get("nulls_first")
    1468        nulls_last = not nulls_first
    1469        nulls_are_large = self.null_ordering == "nulls_are_large"
    1470        nulls_are_small = self.null_ordering == "nulls_are_small"
    1471        nulls_are_last = self.null_ordering == "nulls_are_last"
    1472
    1473        sort_order = " DESC" if desc else ""
    1474        nulls_sort_change = ""
    1475        if nulls_first and (
    1476            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1477        ):
    1478            nulls_sort_change = " NULLS FIRST"
    1479        elif (
    1480            nulls_last
    1481            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1482            and not nulls_are_last
    1483        ):
    1484            nulls_sort_change = " NULLS LAST"
    1485
    1486        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1487            self.unsupported(
    1488                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1489            )
    1490            nulls_sort_change = ""
    1491
    1492        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
    1493
    1494    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1495        partition = self.partition_by_sql(expression)
    1496        order = self.sql(expression, "order")
    1497        measures = self.expressions(expression, key="measures")
    1498        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1499        rows = self.sql(expression, "rows")
    1500        rows = self.seg(rows) if rows else ""
    1501        after = self.sql(expression, "after")
    1502        after = self.seg(after) if after else ""
    1503        pattern = self.sql(expression, "pattern")
    1504        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1505        definition_sqls = [
    1506            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1507            for definition in expression.args.get("define", [])
    1508        ]
    1509        definitions = self.expressions(sqls=definition_sqls)
    1510        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1511        body = "".join(
    1512            (
    1513                partition,
    1514                order,
    1515                measures,
    1516                rows,
    1517                after,
    1518                pattern,
    1519                define,
    1520            )
    1521        )
    1522        alias = self.sql(expression, "alias")
    1523        alias = f" {alias}" if alias else ""
    1524        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    1525
    1526    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1527        limit = expression.args.get("limit")
    1528
    1529        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1530            limit = exp.Limit(expression=limit.args.get("count"))
    1531        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1532            limit = exp.Fetch(direction="FIRST", count=limit.expression)
    1533
    1534        fetch = isinstance(limit, exp.Fetch)
    1535
    1536        return csv(
    1537            *sqls,
    1538            *[self.sql(join) for join in expression.args.get("joins") or []],
    1539            self.sql(expression, "match"),
    1540            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1541            self.sql(expression, "where"),
    1542            self.sql(expression, "group"),
    1543            self.sql(expression, "having"),
    1544            *self.after_having_modifiers(expression),
    1545            self.sql(expression, "order"),
    1546            self.sql(expression, "offset") if fetch else self.sql(limit),
    1547            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1548            *self.after_limit_modifiers(expression),
    1549            sep="",
    1550        )
    1551
    1552    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1553        return [
    1554            self.sql(expression, "qualify"),
    1555            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1556            if expression.args.get("windows")
    1557            else "",
    1558        ]
    1559
    1560    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1561        locks = self.expressions(expression, key="locks", sep=" ")
    1562        locks = f" {locks}" if locks else ""
    1563        return [locks, self.sql(expression, "sample")]
    1564
    1565    def select_sql(self, expression: exp.Select) -> str:
    1566        hint = self.sql(expression, "hint")
    1567        distinct = self.sql(expression, "distinct")
    1568        distinct = f" {distinct}" if distinct else ""
    1569        kind = expression.args.get("kind")
    1570        kind = f" AS {kind}" if kind else ""
    1571        expressions = self.expressions(expression)
    1572        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1573        sql = self.query_modifiers(
    1574            expression,
    1575            f"SELECT{hint}{distinct}{kind}{expressions}",
    1576            self.sql(expression, "into", comment=False),
    1577            self.sql(expression, "from", comment=False),
    1578        )
    1579        return self.prepend_ctes(expression, sql)
    1580
    1581    def schema_sql(self, expression: exp.Schema) -> str:
    1582        this = self.sql(expression, "this")
    1583        this = f"{this} " if this else ""
    1584        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1585        return f"{this}{sql}"
    1586
    1587    def star_sql(self, expression: exp.Star) -> str:
    1588        except_ = self.expressions(expression, key="except", flat=True)
    1589        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1590        replace = self.expressions(expression, key="replace", flat=True)
    1591        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1592        return f"*{except_}{replace}"
    1593
    1594    def parameter_sql(self, expression: exp.Parameter) -> str:
    1595        this = self.sql(expression, "this")
    1596        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1597        return f"{self.PARAMETER_TOKEN}{this}"
    1598
    1599    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1600        this = self.sql(expression, "this")
    1601        kind = expression.text("kind")
    1602        if kind:
    1603            kind = f"{kind}."
    1604        return f"@@{kind}{this}"
    1605
    1606    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    1607        return f":{expression.name}" if expression.name else "?"
    1608
    1609    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1610        alias = self.sql(expression, "alias")
    1611        alias = f"{sep}{alias}" if alias else ""
    1612
    1613        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1614        pivots = f" {pivots}" if pivots else ""
    1615
    1616        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    1617        return self.prepend_ctes(expression, sql)
    1618
    1619    def qualify_sql(self, expression: exp.Qualify) -> str:
    1620        this = self.indent(self.sql(expression, "this"))
    1621        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    1622
    1623    def union_sql(self, expression: exp.Union) -> str:
    1624        return self.prepend_ctes(
    1625            expression,
    1626            self.set_operation(expression, self.union_op(expression)),
    1627        )
    1628
    1629    def union_op(self, expression: exp.Union) -> str:
    1630        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1631        kind = kind if expression.args.get("distinct") else " ALL"
    1632        return f"UNION{kind}"
    1633
    1634    def unnest_sql(self, expression: exp.Unnest) -> str:
    1635        args = self.expressions(expression, flat=True)
    1636        alias = expression.args.get("alias")
    1637        if alias and self.unnest_column_only:
    1638            columns = alias.columns
    1639            alias = self.sql(columns[0]) if columns else ""
    1640        else:
    1641            alias = self.sql(expression, "alias")
    1642        alias = f" AS {alias}" if alias else alias
    1643        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1644        offset = expression.args.get("offset")
    1645        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1646        return f"UNNEST({args}){ordinality}{alias}{offset}"
    1647
    1648    def where_sql(self, expression: exp.Where) -> str:
    1649        this = self.indent(self.sql(expression, "this"))
    1650        return f"{self.seg('WHERE')}{self.sep()}{this}"
    1651
    1652    def window_sql(self, expression: exp.Window) -> str:
    1653        this = self.sql(expression, "this")
    1654        partition = self.partition_by_sql(expression)
    1655        order = expression.args.get("order")
    1656        order = self.order_sql(order, flat=True) if order else ""
    1657        spec = self.sql(expression, "spec")
    1658        alias = self.sql(expression, "alias")
    1659        over = self.sql(expression, "over") or "OVER"
    1660
    1661        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    1662
    1663        first = expression.args.get("first")
    1664        if first is None:
    1665            first = ""
    1666        else:
    1667            first = "FIRST" if first else "LAST"
    1668
    1669        if not partition and not order and not spec and alias:
    1670            return f"{this} {alias}"
    1671
    1672        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    1673        return f"{this} ({args})"
    1674
    1675    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    1676        partition = self.expressions(expression, key="partition_by", flat=True)
    1677        return f"PARTITION BY {partition}" if partition else ""
    1678
    1679    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    1680        kind = self.sql(expression, "kind")
    1681        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1682        end = (
    1683            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1684            or "CURRENT ROW"
    1685        )
    1686        return f"{kind} BETWEEN {start} AND {end}"
    1687
    1688    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    1689        this = self.sql(expression, "this")
    1690        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    1691        return f"{this} WITHIN GROUP ({expression_sql})"
    1692
    1693    def between_sql(self, expression: exp.Between) -> str:
    1694        this = self.sql(expression, "this")
    1695        low = self.sql(expression, "low")
    1696        high = self.sql(expression, "high")
    1697        return f"{this} BETWEEN {low} AND {high}"
    1698
    1699    def bracket_sql(self, expression: exp.Bracket) -> str:
    1700        expressions = apply_index_offset(expression.this, expression.expressions, self.index_offset)
    1701        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1702
    1703        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    1704
    1705    def all_sql(self, expression: exp.All) -> str:
    1706        return f"ALL {self.wrap(expression)}"
    1707
    1708    def any_sql(self, expression: exp.Any) -> str:
    1709        this = self.sql(expression, "this")
    1710        if isinstance(expression.this, exp.Subqueryable):
    1711            this = self.wrap(this)
    1712        return f"ANY {this}"
    1713
    1714    def exists_sql(self, expression: exp.Exists) -> str:
    1715        return f"EXISTS{self.wrap(expression)}"
    1716
    1717    def case_sql(self, expression: exp.Case) -> str:
    1718        this = self.sql(expression, "this")
    1719        statements = [f"CASE {this}" if this else "CASE"]
    1720
    1721        for e in expression.args["ifs"]:
    1722            statements.append(f"WHEN {self.sql(e, 'this')}")
    1723            statements.append(f"THEN {self.sql(e, 'true')}")
    1724
    1725        default = self.sql(expression, "default")
    1726
    1727        if default:
    1728            statements.append(f"ELSE {default}")
    1729
    1730        statements.append("END")
    1731
    1732        if self.pretty and self.text_width(statements) > self._max_text_width:
    1733            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1734
    1735        return " ".join(statements)
    1736
    1737    def constraint_sql(self, expression: exp.Constraint) -> str:
    1738        this = self.sql(expression, "this")
    1739        expressions = self.expressions(expression, flat=True)
    1740        return f"CONSTRAINT {this} {expressions}"
    1741
    1742    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    1743        order = expression.args.get("order")
    1744        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    1745        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    1746
    1747    def extract_sql(self, expression: exp.Extract) -> str:
    1748        this = self.sql(expression, "this")
    1749        expression_sql = self.sql(expression, "expression")
    1750        return f"EXTRACT({this} FROM {expression_sql})"
    1751
    1752    def trim_sql(self, expression: exp.Trim) -> str:
    1753        trim_type = self.sql(expression, "position")
    1754
    1755        if trim_type == "LEADING":
    1756            return self.func("LTRIM", expression.this)
    1757        elif trim_type == "TRAILING":
    1758            return self.func("RTRIM", expression.this)
    1759        else:
    1760            return self.func("TRIM", expression.this, expression.expression)
    1761
    1762    def concat_sql(self, expression: exp.Concat) -> str:
    1763        if len(expression.expressions) == 1:
    1764            return self.sql(expression.expressions[0])
    1765        return self.function_fallback_sql(expression)
    1766
    1767    def check_sql(self, expression: exp.Check) -> str:
    1768        this = self.sql(expression, key="this")
    1769        return f"CHECK ({this})"
    1770
    1771    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1772        expressions = self.expressions(expression, flat=True)
    1773        reference = self.sql(expression, "reference")
    1774        reference = f" {reference}" if reference else ""
    1775        delete = self.sql(expression, "delete")
    1776        delete = f" ON DELETE {delete}" if delete else ""
    1777        update = self.sql(expression, "update")
    1778        update = f" ON UPDATE {update}" if update else ""
    1779        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
    1780
    1781    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1782        expressions = self.expressions(expression, flat=True)
    1783        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1784        options = f" {options}" if options else ""
    1785        return f"PRIMARY KEY ({expressions}){options}"
    1786
    1787    def if_sql(self, expression: exp.If) -> str:
    1788        return self.case_sql(
    1789            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1790        )
    1791
    1792    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    1793        modifier = expression.args.get("modifier")
    1794        modifier = f" {modifier}" if modifier else ""
    1795        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    1796
    1797    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    1798        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
    1799
    1800    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    1801        expressions = self.expressions(expression)
    1802        null_handling = expression.args.get("null_handling")
    1803        null_handling = f" {null_handling}" if null_handling else ""
    1804        unique_keys = expression.args.get("unique_keys")
    1805        if unique_keys is not None:
    1806            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    1807        else:
    1808            unique_keys = ""
    1809        return_type = self.sql(expression, "return_type")
    1810        return_type = f" RETURNING {return_type}" if return_type else ""
    1811        format_json = " FORMAT JSON" if expression.args.get("format_json") else ""
    1812        encoding = self.sql(expression, "encoding")
    1813        encoding = f" ENCODING {encoding}" if encoding else ""
    1814        return f"JSON_OBJECT({expressions}{null_handling}{unique_keys}{return_type}{format_json}{encoding})"
    1815
    1816    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    1817        this = self.sql(expression, "this")
    1818        kind = self.sql(expression, "kind")
    1819        path = self.sql(expression, "path")
    1820        path = f" {path}" if path else ""
    1821        as_json = " AS JSON" if expression.args.get("as_json") else ""
    1822        return f"{this} {kind}{path}{as_json}"
    1823
    1824    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    1825        this = self.sql(expression, "this")
    1826        path = self.sql(expression, "path")
    1827        path = f", {path}" if path else ""
    1828        expressions = self.expressions(expression)
    1829        with_ = (
    1830            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    1831            if expressions
    1832            else ""
    1833        )
    1834        return f"OPENJSON({this}{path}){with_}"
    1835
    1836    def in_sql(self, expression: exp.In) -> str:
    1837        query = expression.args.get("query")
    1838        unnest = expression.args.get("unnest")
    1839        field = expression.args.get("field")
    1840        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1841
    1842        if query:
    1843            in_sql = self.wrap(query)
    1844        elif unnest:
    1845            in_sql = self.in_unnest_op(unnest)
    1846        elif field:
    1847            in_sql = self.sql(field)
    1848        else:
    1849            in_sql = f"({self.expressions(expression, flat=True)})"
    1850
    1851        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    1852
    1853    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    1854        return f"(SELECT {self.sql(unnest)})"
    1855
    1856    def interval_sql(self, expression: exp.Interval) -> str:
    1857        unit = self.sql(expression, "unit")
    1858        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    1859            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    1860        unit = f" {unit}" if unit else ""
    1861
    1862        if self.SINGLE_STRING_INTERVAL:
    1863            this = expression.this.name if expression.this else ""
    1864            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    1865
    1866        this = self.sql(expression, "this")
    1867        if this:
    1868            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    1869            this = f" {this}" if unwrapped else f" ({this})"
    1870
    1871        return f"INTERVAL{this}{unit}"
    1872
    1873    def return_sql(self, expression: exp.Return) -> str:
    1874        return f"RETURN {self.sql(expression, 'this')}"
    1875
    1876    def reference_sql(self, expression: exp.Reference) -> str:
    1877        this = self.sql(expression, "this")
    1878        expressions = self.expressions(expression, flat=True)
    1879        expressions = f"({expressions})" if expressions else ""
    1880        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1881        options = f" {options}" if options else ""
    1882        return f"REFERENCES {this}{expressions}{options}"
    1883
    1884    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    1885        return self.func(expression.name, *expression.expressions)
    1886
    1887    def paren_sql(self, expression: exp.Paren) -> str:
    1888        if isinstance(expression.unnest(), exp.Select):
    1889            sql = self.wrap(expression)
    1890        else:
    1891            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1892            sql = f"({sql}{self.seg(')', sep='')}"
    1893
    1894        return self.prepend_ctes(expression, sql)
    1895
    1896    def neg_sql(self, expression: exp.Neg) -> str:
    1897        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    1898        this_sql = self.sql(expression, "this")
    1899        sep = " " if this_sql[0] == "-" else ""
    1900        return f"-{sep}{this_sql}"
    1901
    1902    def not_sql(self, expression: exp.Not) -> str:
    1903        return f"NOT {self.sql(expression, 'this')}"
    1904
    1905    def alias_sql(self, expression: exp.Alias) -> str:
    1906        alias = self.sql(expression, "alias")
    1907        alias = f" AS {alias}" if alias else ""
    1908        return f"{self.sql(expression, 'this')}{alias}"
    1909
    1910    def aliases_sql(self, expression: exp.Aliases) -> str:
    1911        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    1912
    1913    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1914        this = self.sql(expression, "this")
    1915        zone = self.sql(expression, "zone")
    1916        return f"{this} AT TIME ZONE {zone}"
    1917
    1918    def add_sql(self, expression: exp.Add) -> str:
    1919        return self.binary(expression, "+")
    1920
    1921    def and_sql(self, expression: exp.And) -> str:
    1922        return self.connector_sql(expression, "AND")
    1923
    1924    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1925        if not self.pretty:
    1926            return self.binary(expression, op)
    1927
    1928        sqls = tuple(
    1929            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    1930            for i, e in enumerate(expression.flatten(unnest=False))
    1931        )
    1932
    1933        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1934        return f"{sep}{op} ".join(sqls)
    1935
    1936    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    1937        return self.binary(expression, "&")
    1938
    1939    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    1940        return self.binary(expression, "<<")
    1941
    1942    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    1943        return f"~{self.sql(expression, 'this')}"
    1944
    1945    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    1946        return self.binary(expression, "|")
    1947
    1948    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    1949        return self.binary(expression, ">>")
    1950
    1951    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    1952        return self.binary(expression, "^")
    1953
    1954    def cast_sql(self, expression: exp.Cast) -> str:
    1955        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
    1956
    1957    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    1958        zone = self.sql(expression, "this")
    1959        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    1960
    1961    def collate_sql(self, expression: exp.Collate) -> str:
    1962        return self.binary(expression, "COLLATE")
    1963
    1964    def command_sql(self, expression: exp.Command) -> str:
    1965        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
    1966
    1967    def comment_sql(self, expression: exp.Comment) -> str:
    1968        this = self.sql(expression, "this")
    1969        kind = expression.args["kind"]
    1970        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1971        expression_sql = self.sql(expression, "expression")
    1972        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
    1973
    1974    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    1975        this = self.sql(expression, "this")
    1976        delete = " DELETE" if expression.args.get("delete") else ""
    1977        recompress = self.sql(expression, "recompress")
    1978        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    1979        to_disk = self.sql(expression, "to_disk")
    1980        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    1981        to_volume = self.sql(expression, "to_volume")
    1982        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    1983        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    1984
    1985    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    1986        where = self.sql(expression, "where")
    1987        group = self.sql(expression, "group")
    1988        aggregates = self.expressions(expression, key="aggregates")
    1989        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    1990
    1991        if not (where or group or aggregates) and len(expression.expressions) == 1:
    1992            return f"TTL {self.expressions(expression, flat=True)}"
    1993
    1994        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    1995
    1996    def transaction_sql(self, expression: exp.Transaction) -> str:
    1997        return "BEGIN"
    1998
    1999    def commit_sql(self, expression: exp.Commit) -> str:
    2000        chain = expression.args.get("chain")
    2001        if chain is not None:
    2002            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2003
    2004        return f"COMMIT{chain or ''}"
    2005
    2006    def rollback_sql(self, expression: exp.Rollback) -> str:
    2007        savepoint = expression.args.get("savepoint")
    2008        savepoint = f" TO {savepoint}" if savepoint else ""
    2009        return f"ROLLBACK{savepoint}"
    2010
    2011    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2012        this = self.sql(expression, "this")
    2013
    2014        dtype = self.sql(expression, "dtype")
    2015        if dtype:
    2016            collate = self.sql(expression, "collate")
    2017            collate = f" COLLATE {collate}" if collate else ""
    2018            using = self.sql(expression, "using")
    2019            using = f" USING {using}" if using else ""
    2020            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    2021
    2022        default = self.sql(expression, "default")
    2023        if default:
    2024            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2025
    2026        if not expression.args.get("drop"):
    2027            self.unsupported("Unsupported ALTER COLUMN syntax")
    2028
    2029        return f"ALTER COLUMN {this} DROP DEFAULT"
    2030
    2031    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2032        if not self.RENAME_TABLE_WITH_DB:
    2033            # Remove db from tables
    2034            expression = expression.transform(
    2035                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2036            )
    2037        this = self.sql(expression, "this")
    2038        return f"RENAME TO {this}"
    2039
    2040    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2041        actions = expression.args["actions"]
    2042
    2043        if isinstance(actions[0], exp.ColumnDef):
    2044            actions = self.expressions(expression, key="actions", prefix="ADD COLUMN ")
    2045        elif isinstance(actions[0], exp.Schema):
    2046            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2047        elif isinstance(actions[0], exp.Delete):
    2048            actions = self.expressions(expression, key="actions", flat=True)
    2049        else:
    2050            actions = self.expressions(expression, key="actions")
    2051
    2052        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2053        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
    2054
    2055    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2056        expressions = self.expressions(expression)
    2057        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2058        return f"DROP{exists}{expressions}"
    2059
    2060    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2061        this = self.sql(expression, "this")
    2062        expression_ = self.sql(expression, "expression")
    2063        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2064
    2065        enforced = expression.args.get("enforced")
    2066        if enforced is not None:
    2067            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2068
    2069        return f"{add_constraint} {expression_}"
    2070
    2071    def distinct_sql(self, expression: exp.Distinct) -> str:
    2072        this = self.expressions(expression, flat=True)
    2073        this = f" {this}" if this else ""
    2074
    2075        on = self.sql(expression, "on")
    2076        on = f" ON {on}" if on else ""
    2077        return f"DISTINCT{this}{on}"
    2078
    2079    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    2080        return f"{self.sql(expression, 'this')} IGNORE NULLS"
    2081
    2082    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    2083        return f"{self.sql(expression, 'this')} RESPECT NULLS"
    2084
    2085    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2086        return self.sql(
    2087            exp.Cast(
    2088                this=exp.Div(this=expression.this, expression=expression.expression),
    2089                to=exp.DataType(this=exp.DataType.Type.INT),
    2090            )
    2091        )
    2092
    2093    def dpipe_sql(self, expression: exp.DPipe) -> str:
    2094        return self.binary(expression, "||")
    2095
    2096    def div_sql(self, expression: exp.Div) -> str:
    2097        return self.binary(expression, "/")
    2098
    2099    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    2100        return self.binary(expression, "OVERLAPS")
    2101
    2102    def distance_sql(self, expression: exp.Distance) -> str:
    2103        return self.binary(expression, "<->")
    2104
    2105    def dot_sql(self, expression: exp.Dot) -> str:
    2106        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    2107
    2108    def eq_sql(self, expression: exp.EQ) -> str:
    2109        return self.binary(expression, "=")
    2110
    2111    def escape_sql(self, expression: exp.Escape) -> str:
    2112        return self.binary(expression, "ESCAPE")
    2113
    2114    def glob_sql(self, expression: exp.Glob) -> str:
    2115        return self.binary(expression, "GLOB")
    2116
    2117    def gt_sql(self, expression: exp.GT) -> str:
    2118        return self.binary(expression, ">")
    2119
    2120    def gte_sql(self, expression: exp.GTE) -> str:
    2121        return self.binary(expression, ">=")
    2122
    2123    def ilike_sql(self, expression: exp.ILike) -> str:
    2124        return self.binary(expression, "ILIKE")
    2125
    2126    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    2127        return self.binary(expression, "ILIKE ANY")
    2128
    2129    def is_sql(self, expression: exp.Is) -> str:
    2130        return self.binary(expression, "IS")
    2131
    2132    def like_sql(self, expression: exp.Like) -> str:
    2133        return self.binary(expression, "LIKE")
    2134
    2135    def likeany_sql(self, expression: exp.LikeAny) -> str:
    2136        return self.binary(expression, "LIKE ANY")
    2137
    2138    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    2139        return self.binary(expression, "SIMILAR TO")
    2140
    2141    def lt_sql(self, expression: exp.LT) -> str:
    2142        return self.binary(expression, "<")
    2143
    2144    def lte_sql(self, expression: exp.LTE) -> str:
    2145        return self.binary(expression, "<=")
    2146
    2147    def mod_sql(self, expression: exp.Mod) -> str:
    2148        return self.binary(expression, "%")
    2149
    2150    def mul_sql(self, expression: exp.Mul) -> str:
    2151        return self.binary(expression, "*")
    2152
    2153    def neq_sql(self, expression: exp.NEQ) -> str:
    2154        return self.binary(expression, "<>")
    2155
    2156    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    2157        return self.binary(expression, "IS NOT DISTINCT FROM")
    2158
    2159    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    2160        return self.binary(expression, "IS DISTINCT FROM")
    2161
    2162    def or_sql(self, expression: exp.Or) -> str:
    2163        return self.connector_sql(expression, "OR")
    2164
    2165    def slice_sql(self, expression: exp.Slice) -> str:
    2166        return self.binary(expression, ":")
    2167
    2168    def sub_sql(self, expression: exp.Sub) -> str:
    2169        return self.binary(expression, "-")
    2170
    2171    def trycast_sql(self, expression: exp.TryCast) -> str:
    2172        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
    2173
    2174    def use_sql(self, expression: exp.Use) -> str:
    2175        kind = self.sql(expression, "kind")
    2176        kind = f" {kind}" if kind else ""
    2177        this = self.sql(expression, "this")
    2178        this = f" {this}" if this else ""
    2179        return f"USE{kind}{this}"
    2180
    2181    def binary(self, expression: exp.Binary, op: str) -> str:
    2182        op = self.maybe_comment(op, comments=expression.comments)
    2183        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
    2184
    2185    def function_fallback_sql(self, expression: exp.Func) -> str:
    2186        args = []
    2187        for arg_value in expression.args.values():
    2188            if isinstance(arg_value, list):
    2189                for value in arg_value:
    2190                    args.append(value)
    2191            else:
    2192                args.append(arg_value)
    2193
    2194        return self.func(expression.sql_name(), *args)
    2195
    2196    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
    2197        return f"{self.normalize_func(name)}({self.format_args(*args)})"
    2198
    2199    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    2200        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    2201        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
    2202            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    2203        return ", ".join(arg_sqls)
    2204
    2205    def text_width(self, args: t.Iterable) -> int:
    2206        return sum(len(arg) for arg in args)
    2207
    2208    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2209        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)
    2210
    2211    def expressions(
    2212        self,
    2213        expression: t.Optional[exp.Expression] = None,
    2214        key: t.Optional[str] = None,
    2215        sqls: t.Optional[t.List[str]] = None,
    2216        flat: bool = False,
    2217        indent: bool = True,
    2218        sep: str = ", ",
    2219        prefix: str = "",
    2220    ) -> str:
    2221        expressions = expression.args.get(key or "expressions") if expression else sqls
    2222
    2223        if not expressions:
    2224            return ""
    2225
    2226        if flat:
    2227            return sep.join(self.sql(e) for e in expressions)
    2228
    2229        num_sqls = len(expressions)
    2230
    2231        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2232        pad = " " * self.pad
    2233        stripped_sep = sep.strip()
    2234
    2235        result_sqls = []
    2236        for i, e in enumerate(expressions):
    2237            sql = self.sql(e, comment=False)
    2238            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2239
    2240            if self.pretty:
    2241                if self._leading_comma:
    2242                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2243                else:
    2244                    result_sqls.append(
    2245                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2246                    )
    2247            else:
    2248                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2249
    2250        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2251        return self.indent(result_sql, skip_first=False) if indent else result_sql
    2252
    2253    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2254        flat = flat or isinstance(expression.parent, exp.Properties)
    2255        expressions_sql = self.expressions(expression, flat=flat)
    2256        if flat:
    2257            return f"{op} {expressions_sql}"
    2258        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    2259
    2260    def naked_property(self, expression: exp.Property) -> str:
    2261        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2262        if not property_name:
    2263            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2264        return f"{property_name} {self.sql(expression, 'this')}"
    2265
    2266    def set_operation(self, expression: exp.Expression, op: str) -> str:
    2267        this = self.sql(expression, "this")
    2268        op = self.seg(op)
    2269        return self.query_modifiers(
    2270            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2271        )
    2272
    2273    def tag_sql(self, expression: exp.Tag) -> str:
    2274        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    2275
    2276    def token_sql(self, token_type: TokenType) -> str:
    2277        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    2278
    2279    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2280        this = self.sql(expression, "this")
    2281        expressions = self.no_identify(self.expressions, expression)
    2282        expressions = (
    2283            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2284        )
    2285        return f"{this}{expressions}"
    2286
    2287    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2288        this = self.sql(expression, "this")
    2289        expressions = self.expressions(expression, flat=True)
    2290        return f"{this}({expressions})"
    2291
    2292    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    2293        return self.binary(expression, "=>")
    2294
    2295    def when_sql(self, expression: exp.When) -> str:
    2296        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2297        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2298        condition = self.sql(expression, "condition")
    2299        condition = f" AND {condition}" if condition else ""
    2300
    2301        then_expression = expression.args.get("then")
    2302        if isinstance(then_expression, exp.Insert):
    2303            then = f"INSERT {self.sql(then_expression, 'this')}"
    2304            if "expression" in then_expression.args:
    2305                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2306        elif isinstance(then_expression, exp.Update):
    2307            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2308                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2309            else:
    2310                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2311        else:
    2312            then = self.sql(then_expression)
    2313        return f"WHEN {matched}{source}{condition} THEN {then}"
    2314
    2315    def merge_sql(self, expression: exp.Merge) -> str:
    2316        this = self.sql(expression, "this")
    2317        using = f"USING {self.sql(expression, 'using')}"
    2318        on = f"ON {self.sql(expression, 'on')}"
    2319        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"
    2320
    2321    def tochar_sql(self, expression: exp.ToChar) -> str:
    2322        if expression.args.get("format"):
    2323            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2324
    2325        return self.sql(exp.cast(expression.this, "text"))
    2326
    2327    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2328        this = self.sql(expression, "this")
    2329        kind = self.sql(expression, "kind")
    2330        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2331        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2332        return f"{this}({kind}{args})"
    2333
    2334    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2335        this = self.sql(expression, "this")
    2336        max = self.sql(expression, "max")
    2337        min = self.sql(expression, "min")
    2338        return f"{this}(MIN {min} MAX {max})"
    2339
    2340    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    2341        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    2342
    2343
    2344def cached_generator(
    2345    cache: t.Optional[t.Dict[int, str]] = None
    2346) -> t.Callable[[exp.Expression], str]:
    2347    """Returns a cached generator."""
    2348    cache = {} if cache is None else cache
    2349    generator = Generator(normalize=True, identify="safe")
    2350    return lambda e: generator.generate(e, cache)

class Generator: View Source

      16class Generator:
      17    """
      18    Generator interprets the given syntax tree and produces a SQL string as an output.
      19
      20    Args:
      21        time_mapping (dict): the dictionary of custom time mappings in which the key
      22            represents a python time format and the output the target time format
      23        time_trie (trie): a trie of the time_mapping keys
      24        pretty (bool): if set to True the returned string will be formatted. Default: False.
      25        quote_start (str): specifies which starting character to use to delimit quotes. Default: '.
      26        quote_end (str): specifies which ending character to use to delimit quotes. Default: '.
      27        identifier_start (str): specifies which starting character to use to delimit identifiers. Default: ".
      28        identifier_end (str): specifies which ending character to use to delimit identifiers. Default: ".
      29        bit_start (str): specifies which starting character to use to delimit bit literals. Default: None.
      30        bit_end (str): specifies which ending character to use to delimit bit literals. Default: None.
      31        hex_start (str): specifies which starting character to use to delimit hex literals. Default: None.
      32        hex_end (str): specifies which ending character to use to delimit hex literals. Default: None.
      33        byte_start (str): specifies which starting character to use to delimit byte literals. Default: None.
      34        byte_end (str): specifies which ending character to use to delimit byte literals. Default: None.
      35        raw_start (str): specifies which starting character to use to delimit raw literals. Default: None.
      36        raw_end (str): specifies which ending character to use to delimit raw literals. Default: None.
      37        identify (bool | str): 'always': always quote, 'safe': quote identifiers if they don't contain an upcase, True defaults to always.
      38        normalize (bool): if set to True all identifiers will lower cased
      39        string_escape (str): specifies a string escape character. Default: '.
      40        identifier_escape (str): specifies an identifier escape character. Default: ".
      41        pad (int): determines padding in a formatted string. Default: 2.
      42        indent (int): determines the size of indentation in a formatted string. Default: 4.
      43        unnest_column_only (bool): if true unnest table aliases are considered only as column aliases
      44        normalize_functions (str): normalize function names, "upper", "lower", or None
      45            Default: "upper"
      46        alias_post_tablesample (bool): if the table alias comes after tablesample
      47            Default: False
      48        identifiers_can_start_with_digit (bool): if an unquoted identifier can start with digit
      49            Default: False
      50        unsupported_level (ErrorLevel): determines the generator's behavior when it encounters
      51            unsupported expressions. Default ErrorLevel.WARN.
      52        null_ordering (str): Indicates the default null ordering method to use if not explicitly set.
      53            Options are "nulls_are_small", "nulls_are_large", "nulls_are_last".
      54            Default: "nulls_are_small"
      55        max_unsupported (int): Maximum number of unsupported messages to include in a raised UnsupportedError.
      56            This is only relevant if unsupported_level is ErrorLevel.RAISE.
      57            Default: 3
      58        leading_comma (bool): if the the comma is leading or trailing in select statements
      59            Default: False
      60        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
      61            The default is on the smaller end because the length only represents a segment and not the true
      62            line length.
      63            Default: 80
      64        comments: Whether or not to preserve comments in the output SQL code.
      65            Default: True
      66    """
      67
      68    TRANSFORMS = {
      69        exp.DateAdd: lambda self, e: self.func(
      70            "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      71        ),
      72        exp.TsOrDsAdd: lambda self, e: self.func(
      73            "TS_OR_DS_ADD", e.this, e.expression, exp.Literal.string(e.text("unit"))
      74        ),
      75        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
      76        exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
      77        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
      78        exp.ExternalProperty: lambda self, e: "EXTERNAL",
      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.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
      85        exp.ReturnsProperty: lambda self, e: self.naked_property(e),
      86        exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
      87        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
      88        exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
      89        exp.TemporaryProperty: lambda self, e: f"TEMPORARY",
      90        exp.TransientProperty: lambda self, e: "TRANSIENT",
      91        exp.StabilityProperty: lambda self, e: e.name,
      92        exp.VolatileProperty: lambda self, e: "VOLATILE",
      93        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
      94        exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
      95        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
      96        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
      97        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
      98        exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE",
      99        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     100        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
     101        exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})",
     102        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
     103        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
     104        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
     105        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
     106        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {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 a table is allowed to be renamed with a db
     143    RENAME_TABLE_WITH_DB = True
     144
     145    # The separator for grouping sets and rollups
     146    GROUPINGS_SEP = ","
     147
     148    # The string used for creating index on a table
     149    INDEX_ON = "ON"
     150
     151    TYPE_MAPPING = {
     152        exp.DataType.Type.NCHAR: "CHAR",
     153        exp.DataType.Type.NVARCHAR: "VARCHAR",
     154        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     155        exp.DataType.Type.LONGTEXT: "TEXT",
     156        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     157        exp.DataType.Type.LONGBLOB: "BLOB",
     158        exp.DataType.Type.INET: "INET",
     159    }
     160
     161    STAR_MAPPING = {
     162        "except": "EXCEPT",
     163        "replace": "REPLACE",
     164    }
     165
     166    TIME_PART_SINGULARS = {
     167        "microseconds": "microsecond",
     168        "seconds": "second",
     169        "minutes": "minute",
     170        "hours": "hour",
     171        "days": "day",
     172        "weeks": "week",
     173        "months": "month",
     174        "quarters": "quarter",
     175        "years": "year",
     176    }
     177
     178    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     179
     180    STRUCT_DELIMITER = ("<", ">")
     181
     182    PARAMETER_TOKEN = "@"
     183
     184    PROPERTIES_LOCATION = {
     185        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     186        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     187        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     188        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     189        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     190        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     191        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     192        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     193        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     194        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     195        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     196        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     197        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     198        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     199        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     200        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     201        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     202        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     203        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     204        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     205        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     206        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     207        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     208        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     209        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     210        exp.LogProperty: exp.Properties.Location.POST_NAME,
     211        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     212        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     213        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     214        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     215        exp.Order: exp.Properties.Location.POST_SCHEMA,
     216        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     217        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     218        exp.Property: exp.Properties.Location.POST_WITH,
     219        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     220        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     221        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     222        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     223        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     224        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     225        exp.Set: exp.Properties.Location.POST_SCHEMA,
     226        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     227        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     228        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     229        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     230        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     231        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     232        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     233        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     234        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     235        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     236        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     237    }
     238
     239    JOIN_HINTS = True
     240    TABLE_HINTS = True
     241
     242    RESERVED_KEYWORDS: t.Set[str] = set()
     243    WITH_SEPARATED_COMMENTS = (exp.Select, exp.From, exp.Where, exp.With)
     244    UNWRAPPED_INTERVAL_VALUES = (exp.Column, exp.Literal, exp.Neg, exp.Paren)
     245
     246    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     247
     248    __slots__ = (
     249        "time_mapping",
     250        "time_trie",
     251        "pretty",
     252        "quote_start",
     253        "quote_end",
     254        "identifier_start",
     255        "identifier_end",
     256        "bit_start",
     257        "bit_end",
     258        "hex_start",
     259        "hex_end",
     260        "byte_start",
     261        "byte_end",
     262        "raw_start",
     263        "raw_end",
     264        "identify",
     265        "normalize",
     266        "string_escape",
     267        "identifier_escape",
     268        "pad",
     269        "index_offset",
     270        "unnest_column_only",
     271        "alias_post_tablesample",
     272        "identifiers_can_start_with_digit",
     273        "normalize_functions",
     274        "unsupported_level",
     275        "unsupported_messages",
     276        "null_ordering",
     277        "max_unsupported",
     278        "_indent",
     279        "_escaped_quote_end",
     280        "_escaped_identifier_end",
     281        "_leading_comma",
     282        "_max_text_width",
     283        "_comments",
     284        "_cache",
     285    )
     286
     287    def __init__(
     288        self,
     289        time_mapping=None,
     290        time_trie=None,
     291        pretty=None,
     292        quote_start=None,
     293        quote_end=None,
     294        identifier_start=None,
     295        identifier_end=None,
     296        bit_start=None,
     297        bit_end=None,
     298        hex_start=None,
     299        hex_end=None,
     300        byte_start=None,
     301        byte_end=None,
     302        raw_start=None,
     303        raw_end=None,
     304        identify=False,
     305        normalize=False,
     306        string_escape=None,
     307        identifier_escape=None,
     308        pad=2,
     309        indent=2,
     310        index_offset=0,
     311        unnest_column_only=False,
     312        alias_post_tablesample=False,
     313        identifiers_can_start_with_digit=False,
     314        normalize_functions="upper",
     315        unsupported_level=ErrorLevel.WARN,
     316        null_ordering=None,
     317        max_unsupported=3,
     318        leading_comma=False,
     319        max_text_width=80,
     320        comments=True,
     321    ):
     322        import sqlglot
     323
     324        self.time_mapping = time_mapping or {}
     325        self.time_trie = time_trie
     326        self.pretty = pretty if pretty is not None else sqlglot.pretty
     327        self.quote_start = quote_start or "'"
     328        self.quote_end = quote_end or "'"
     329        self.identifier_start = identifier_start or '"'
     330        self.identifier_end = identifier_end or '"'
     331        self.bit_start = bit_start
     332        self.bit_end = bit_end
     333        self.hex_start = hex_start
     334        self.hex_end = hex_end
     335        self.byte_start = byte_start
     336        self.byte_end = byte_end
     337        self.raw_start = raw_start
     338        self.raw_end = raw_end
     339        self.identify = identify
     340        self.normalize = normalize
     341        self.string_escape = string_escape or "'"
     342        self.identifier_escape = identifier_escape or '"'
     343        self.pad = pad
     344        self.index_offset = index_offset
     345        self.unnest_column_only = unnest_column_only
     346        self.alias_post_tablesample = alias_post_tablesample
     347        self.identifiers_can_start_with_digit = identifiers_can_start_with_digit
     348        self.normalize_functions = normalize_functions
     349        self.unsupported_level = unsupported_level
     350        self.unsupported_messages = []
     351        self.max_unsupported = max_unsupported
     352        self.null_ordering = null_ordering
     353        self._indent = indent
     354        self._escaped_quote_end = self.string_escape + self.quote_end
     355        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
     356        self._leading_comma = leading_comma
     357        self._max_text_width = max_text_width
     358        self._comments = comments
     359        self._cache = None
     360
     361    def generate(
     362        self,
     363        expression: t.Optional[exp.Expression],
     364        cache: t.Optional[t.Dict[int, str]] = None,
     365    ) -> str:
     366        """
     367        Generates a SQL string by interpreting the given syntax tree.
     368
     369        Args
     370            expression: the syntax tree.
     371            cache: an optional sql string cache. this leverages the hash of an expression which is slow, so only use this if you set _hash on each node.
     372
     373        Returns
     374            the SQL string.
     375        """
     376        if cache is not None:
     377            self._cache = cache
     378        self.unsupported_messages = []
     379        sql = self.sql(expression).strip()
     380        self._cache = None
     381
     382        if self.unsupported_level == ErrorLevel.IGNORE:
     383            return sql
     384
     385        if self.unsupported_level == ErrorLevel.WARN:
     386            for msg in self.unsupported_messages:
     387                logger.warning(msg)
     388        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     389            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     390
     391        if self.pretty:
     392            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     393        return sql
     394
     395    def unsupported(self, message: str) -> None:
     396        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     397            raise UnsupportedError(message)
     398        self.unsupported_messages.append(message)
     399
     400    def sep(self, sep: str = " ") -> str:
     401        return f"{sep.strip()}\n" if self.pretty else sep
     402
     403    def seg(self, sql: str, sep: str = " ") -> str:
     404        return f"{self.sep(sep)}{sql}"
     405
     406    def pad_comment(self, comment: str) -> str:
     407        comment = " " + comment if comment[0].strip() else comment
     408        comment = comment + " " if comment[-1].strip() else comment
     409        return comment
     410
     411    def maybe_comment(
     412        self,
     413        sql: str,
     414        expression: t.Optional[exp.Expression] = None,
     415        comments: t.Optional[t.List[str]] = None,
     416    ) -> str:
     417        comments = ((expression and expression.comments) if comments is None else comments) if self._comments else None  # type: ignore
     418
     419        if not comments or isinstance(expression, exp.Binary):
     420            return sql
     421
     422        sep = "\n" if self.pretty else " "
     423        comments_sql = sep.join(
     424            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     425        )
     426
     427        if not comments_sql:
     428            return sql
     429
     430        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     431            return (
     432                f"{self.sep()}{comments_sql}{sql}"
     433                if sql[0].isspace()
     434                else f"{comments_sql}{self.sep()}{sql}"
     435            )
     436
     437        return f"{sql} {comments_sql}"
     438
     439    def wrap(self, expression: exp.Expression | str) -> str:
     440        this_sql = self.indent(
     441            self.sql(expression)
     442            if isinstance(expression, (exp.Select, exp.Union))
     443            else self.sql(expression, "this"),
     444            level=1,
     445            pad=0,
     446        )
     447        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     448
     449    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     450        original = self.identify
     451        self.identify = False
     452        result = func(*args, **kwargs)
     453        self.identify = original
     454        return result
     455
     456    def normalize_func(self, name: str) -> str:
     457        if self.normalize_functions == "upper":
     458            return name.upper()
     459        if self.normalize_functions == "lower":
     460            return name.lower()
     461        return name
     462
     463    def indent(
     464        self,
     465        sql: str,
     466        level: int = 0,
     467        pad: t.Optional[int] = None,
     468        skip_first: bool = False,
     469        skip_last: bool = False,
     470    ) -> str:
     471        if not self.pretty:
     472            return sql
     473
     474        pad = self.pad if pad is None else pad
     475        lines = sql.split("\n")
     476
     477        return "\n".join(
     478            line
     479            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     480            else f"{' ' * (level * self._indent + pad)}{line}"
     481            for i, line in enumerate(lines)
     482        )
     483
     484    def sql(
     485        self,
     486        expression: t.Optional[str | exp.Expression],
     487        key: t.Optional[str] = None,
     488        comment: bool = True,
     489    ) -> str:
     490        if not expression:
     491            return ""
     492
     493        if isinstance(expression, str):
     494            return expression
     495
     496        if key:
     497            return self.sql(expression.args.get(key))
     498
     499        if self._cache is not None:
     500            expression_id = hash(expression)
     501
     502            if expression_id in self._cache:
     503                return self._cache[expression_id]
     504
     505        transform = self.TRANSFORMS.get(expression.__class__)
     506
     507        if callable(transform):
     508            sql = transform(self, expression)
     509        elif transform:
     510            sql = transform
     511        elif isinstance(expression, exp.Expression):
     512            exp_handler_name = f"{expression.key}_sql"
     513
     514            if hasattr(self, exp_handler_name):
     515                sql = getattr(self, exp_handler_name)(expression)
     516            elif isinstance(expression, exp.Func):
     517                sql = self.function_fallback_sql(expression)
     518            elif isinstance(expression, exp.Property):
     519                sql = self.property_sql(expression)
     520            else:
     521                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     522        else:
     523            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     524
     525        sql = self.maybe_comment(sql, expression) if self._comments and comment else sql
     526
     527        if self._cache is not None:
     528            self._cache[expression_id] = sql
     529        return sql
     530
     531    def uncache_sql(self, expression: exp.Uncache) -> str:
     532        table = self.sql(expression, "this")
     533        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     534        return f"UNCACHE TABLE{exists_sql} {table}"
     535
     536    def cache_sql(self, expression: exp.Cache) -> str:
     537        lazy = " LAZY" if expression.args.get("lazy") else ""
     538        table = self.sql(expression, "this")
     539        options = expression.args.get("options")
     540        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     541        sql = self.sql(expression, "expression")
     542        sql = f" AS{self.sep()}{sql}" if sql else ""
     543        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     544        return self.prepend_ctes(expression, sql)
     545
     546    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     547        if isinstance(expression.parent, exp.Cast):
     548            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     549        default = "DEFAULT " if expression.args.get("default") else ""
     550        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     551
     552    def column_sql(self, expression: exp.Column) -> str:
     553        return ".".join(
     554            self.sql(part)
     555            for part in (
     556                expression.args.get("catalog"),
     557                expression.args.get("db"),
     558                expression.args.get("table"),
     559                expression.args.get("this"),
     560            )
     561            if part
     562        )
     563
     564    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     565        this = self.sql(expression, "this")
     566        this = f" {this}" if this else ""
     567        position = self.sql(expression, "position")
     568        return f"{position}{this}"
     569
     570    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     571        column = self.sql(expression, "this")
     572        kind = self.sql(expression, "kind")
     573        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     574        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     575        kind = f"{sep}{kind}" if kind else ""
     576        constraints = f" {constraints}" if constraints else ""
     577        position = self.sql(expression, "position")
     578        position = f" {position}" if position else ""
     579
     580        return f"{exists}{column}{kind}{constraints}{position}"
     581
     582    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     583        this = self.sql(expression, "this")
     584        kind_sql = self.sql(expression, "kind").strip()
     585        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     586
     587    def autoincrementcolumnconstraint_sql(self, _) -> str:
     588        return self.token_sql(TokenType.AUTO_INCREMENT)
     589
     590    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     591        if isinstance(expression.this, list):
     592            this = self.wrap(self.expressions(expression, key="this", flat=True))
     593        else:
     594            this = self.sql(expression, "this")
     595
     596        return f"COMPRESS {this}"
     597
     598    def generatedasidentitycolumnconstraint_sql(
     599        self, expression: exp.GeneratedAsIdentityColumnConstraint
     600    ) -> str:
     601        this = ""
     602        if expression.this is not None:
     603            on_null = "ON NULL " if expression.args.get("on_null") else ""
     604            this = " ALWAYS " if expression.this else f" BY DEFAULT {on_null}"
     605
     606        start = expression.args.get("start")
     607        start = f"START WITH {start}" if start else ""
     608        increment = expression.args.get("increment")
     609        increment = f" INCREMENT BY {increment}" if increment else ""
     610        minvalue = expression.args.get("minvalue")
     611        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
     612        maxvalue = expression.args.get("maxvalue")
     613        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
     614        cycle = expression.args.get("cycle")
     615        cycle_sql = ""
     616
     617        if cycle is not None:
     618            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
     619            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
     620
     621        sequence_opts = ""
     622        if start or increment or cycle_sql:
     623            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
     624            sequence_opts = f" ({sequence_opts.strip()})"
     625
     626        expr = self.sql(expression, "expression")
     627        expr = f"({expr})" if expr else "IDENTITY"
     628
     629        return f"GENERATED{this}AS {expr}{sequence_opts}"
     630
     631    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
     632        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
     633
     634    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
     635        desc = expression.args.get("desc")
     636        if desc is not None:
     637            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
     638        return f"PRIMARY KEY"
     639
     640    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
     641        this = self.sql(expression, "this")
     642        this = f" {this}" if this else ""
     643        return f"UNIQUE{this}"
     644
     645    def create_sql(self, expression: exp.Create) -> str:
     646        kind = self.sql(expression, "kind").upper()
     647        properties = expression.args.get("properties")
     648        properties_exp = expression.copy()
     649        properties_locs = self.locate_properties(properties) if properties else {}
     650        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
     651            exp.Properties.Location.POST_WITH
     652        ):
     653            properties_exp.set(
     654                "properties",
     655                exp.Properties(
     656                    expressions=[
     657                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
     658                        *properties_locs[exp.Properties.Location.POST_WITH],
     659                    ]
     660                ),
     661            )
     662        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
     663            this_name = self.sql(expression.this, "this")
     664            this_properties = self.properties(
     665                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
     666                wrapped=False,
     667            )
     668            this_schema = f"({self.expressions(expression.this)})"
     669            this = f"{this_name}, {this_properties} {this_schema}"
     670            properties_sql = ""
     671        else:
     672            this = self.sql(expression, "this")
     673            properties_sql = self.sql(properties_exp, "properties")
     674        begin = " BEGIN" if expression.args.get("begin") else ""
     675        expression_sql = self.sql(expression, "expression")
     676        if expression_sql:
     677            expression_sql = f"{begin}{self.sep()}{expression_sql}"
     678
     679            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
     680                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
     681                    postalias_props_sql = self.properties(
     682                        exp.Properties(
     683                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
     684                        ),
     685                        wrapped=False,
     686                    )
     687                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
     688                else:
     689                    expression_sql = f" AS{expression_sql}"
     690
     691        postindex_props_sql = ""
     692        if properties_locs.get(exp.Properties.Location.POST_INDEX):
     693            postindex_props_sql = self.properties(
     694                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
     695                wrapped=False,
     696                prefix=" ",
     697            )
     698
     699        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
     700        indexes = f" {indexes}" if indexes else ""
     701        index_sql = indexes + postindex_props_sql
     702
     703        replace = " OR REPLACE" if expression.args.get("replace") else ""
     704        unique = " UNIQUE" if expression.args.get("unique") else ""
     705
     706        postcreate_props_sql = ""
     707        if properties_locs.get(exp.Properties.Location.POST_CREATE):
     708            postcreate_props_sql = self.properties(
     709                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
     710                sep=" ",
     711                prefix=" ",
     712                wrapped=False,
     713            )
     714
     715        modifiers = "".join((replace, unique, postcreate_props_sql))
     716
     717        postexpression_props_sql = ""
     718        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
     719            postexpression_props_sql = self.properties(
     720                exp.Properties(
     721                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
     722                ),
     723                sep=" ",
     724                prefix=" ",
     725                wrapped=False,
     726            )
     727
     728        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
     729        no_schema_binding = (
     730            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
     731        )
     732
     733        clone = self.sql(expression, "clone")
     734        clone = f" {clone}" if clone else ""
     735
     736        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
     737        return self.prepend_ctes(expression, expression_sql)
     738
     739    def clone_sql(self, expression: exp.Clone) -> str:
     740        this = self.sql(expression, "this")
     741        when = self.sql(expression, "when")
     742
     743        if when:
     744            kind = self.sql(expression, "kind")
     745            expr = self.sql(expression, "expression")
     746            return f"CLONE {this} {when} ({kind} => {expr})"
     747
     748        return f"CLONE {this}"
     749
     750    def describe_sql(self, expression: exp.Describe) -> str:
     751        return f"DESCRIBE {self.sql(expression, 'this')}"
     752
     753    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
     754        with_ = self.sql(expression, "with")
     755        if with_:
     756            sql = f"{with_}{self.sep()}{sql}"
     757        return sql
     758
     759    def with_sql(self, expression: exp.With) -> str:
     760        sql = self.expressions(expression, flat=True)
     761        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
     762
     763        return f"WITH {recursive}{sql}"
     764
     765    def cte_sql(self, expression: exp.CTE) -> str:
     766        alias = self.sql(expression, "alias")
     767        return f"{alias} AS {self.wrap(expression)}"
     768
     769    def tablealias_sql(self, expression: exp.TableAlias) -> str:
     770        alias = self.sql(expression, "this")
     771        columns = self.expressions(expression, key="columns", flat=True)
     772        columns = f"({columns})" if columns else ""
     773        return f"{alias}{columns}"
     774
     775    def bitstring_sql(self, expression: exp.BitString) -> str:
     776        this = self.sql(expression, "this")
     777        if self.bit_start:
     778            return f"{self.bit_start}{this}{self.bit_end}"
     779        return f"{int(this, 2)}"
     780
     781    def hexstring_sql(self, expression: exp.HexString) -> str:
     782        this = self.sql(expression, "this")
     783        if self.hex_start:
     784            return f"{self.hex_start}{this}{self.hex_end}"
     785        return f"{int(this, 16)}"
     786
     787    def bytestring_sql(self, expression: exp.ByteString) -> str:
     788        this = self.sql(expression, "this")
     789        if self.byte_start:
     790            return f"{self.byte_start}{this}{self.byte_end}"
     791        return this
     792
     793    def rawstring_sql(self, expression: exp.RawString) -> str:
     794        if self.raw_start:
     795            return f"{self.raw_start}{expression.name}{self.raw_end}"
     796        return self.sql(exp.Literal.string(expression.name.replace("\\", "\\\\")))
     797
     798    def datatypesize_sql(self, expression: exp.DataTypeSize) -> str:
     799        this = self.sql(expression, "this")
     800        specifier = self.sql(expression, "expression")
     801        specifier = f" {specifier}" if specifier else ""
     802        return f"{this}{specifier}"
     803
     804    def datatype_sql(self, expression: exp.DataType) -> str:
     805        type_value = expression.this
     806        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
     807        nested = ""
     808        interior = self.expressions(expression, flat=True)
     809        values = ""
     810        if interior:
     811            if expression.args.get("nested"):
     812                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
     813                if expression.args.get("values") is not None:
     814                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
     815                    values = self.expressions(expression, key="values", flat=True)
     816                    values = f"{delimiters[0]}{values}{delimiters[1]}"
     817            else:
     818                nested = f"({interior})"
     819
     820        return f"{type_sql}{nested}{values}"
     821
     822    def directory_sql(self, expression: exp.Directory) -> str:
     823        local = "LOCAL " if expression.args.get("local") else ""
     824        row_format = self.sql(expression, "row_format")
     825        row_format = f" {row_format}" if row_format else ""
     826        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
     827
     828    def delete_sql(self, expression: exp.Delete) -> str:
     829        this = self.sql(expression, "this")
     830        this = f" FROM {this}" if this else ""
     831        using_sql = (
     832            f" USING {self.expressions(expression, key='using', sep=', USING ')}"
     833            if expression.args.get("using")
     834            else ""
     835        )
     836        where_sql = self.sql(expression, "where")
     837        returning = self.sql(expression, "returning")
     838        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
     839        return self.prepend_ctes(expression, sql)
     840
     841    def drop_sql(self, expression: exp.Drop) -> str:
     842        this = self.sql(expression, "this")
     843        kind = expression.args["kind"]
     844        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
     845        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
     846        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
     847        cascade = " CASCADE" if expression.args.get("cascade") else ""
     848        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
     849        purge = " PURGE" if expression.args.get("purge") else ""
     850        return (
     851            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
     852        )
     853
     854    def except_sql(self, expression: exp.Except) -> str:
     855        return self.prepend_ctes(
     856            expression,
     857            self.set_operation(expression, self.except_op(expression)),
     858        )
     859
     860    def except_op(self, expression: exp.Except) -> str:
     861        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
     862
     863    def fetch_sql(self, expression: exp.Fetch) -> str:
     864        direction = expression.args.get("direction")
     865        direction = f" {direction.upper()}" if direction else ""
     866        count = expression.args.get("count")
     867        count = f" {count}" if count else ""
     868        if expression.args.get("percent"):
     869            count = f"{count} PERCENT"
     870        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
     871        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
     872
     873    def filter_sql(self, expression: exp.Filter) -> str:
     874        this = self.sql(expression, "this")
     875        where = self.sql(expression, "expression")[1:]  # where has a leading space
     876        return f"{this} FILTER({where})"
     877
     878    def hint_sql(self, expression: exp.Hint) -> str:
     879        if self.sql(expression, "this"):
     880            self.unsupported("Hints are not supported")
     881        return ""
     882
     883    def index_sql(self, expression: exp.Index) -> str:
     884        unique = "UNIQUE " if expression.args.get("unique") else ""
     885        primary = "PRIMARY " if expression.args.get("primary") else ""
     886        amp = "AMP " if expression.args.get("amp") else ""
     887        name = f"{expression.name} " if expression.name else ""
     888        table = self.sql(expression, "table")
     889        table = f"{self.INDEX_ON} {table} " if table else ""
     890        index = "INDEX " if not table else ""
     891        columns = self.expressions(expression, key="columns", flat=True)
     892        partition_by = self.expressions(expression, key="partition_by", flat=True)
     893        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
     894        return f"{unique}{primary}{amp}{index}{name}{table}({columns}){partition_by}"
     895
     896    def identifier_sql(self, expression: exp.Identifier) -> str:
     897        text = expression.name
     898        lower = text.lower()
     899        text = lower if self.normalize and not expression.quoted else text
     900        text = text.replace(self.identifier_end, self._escaped_identifier_end)
     901        if (
     902            expression.quoted
     903            or should_identify(text, self.identify)
     904            or lower in self.RESERVED_KEYWORDS
     905            or (not self.identifiers_can_start_with_digit and text[:1].isdigit())
     906        ):
     907            text = f"{self.identifier_start}{text}{self.identifier_end}"
     908        return text
     909
     910    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
     911        input_format = self.sql(expression, "input_format")
     912        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
     913        output_format = self.sql(expression, "output_format")
     914        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
     915        return self.sep().join((input_format, output_format))
     916
     917    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
     918        string = self.sql(exp.Literal.string(expression.name))
     919        return f"{prefix}{string}"
     920
     921    def partition_sql(self, expression: exp.Partition) -> str:
     922        return f"PARTITION({self.expressions(expression)})"
     923
     924    def properties_sql(self, expression: exp.Properties) -> str:
     925        root_properties = []
     926        with_properties = []
     927
     928        for p in expression.expressions:
     929            p_loc = self.PROPERTIES_LOCATION[p.__class__]
     930            if p_loc == exp.Properties.Location.POST_WITH:
     931                with_properties.append(p)
     932            elif p_loc == exp.Properties.Location.POST_SCHEMA:
     933                root_properties.append(p)
     934
     935        return self.root_properties(
     936            exp.Properties(expressions=root_properties)
     937        ) + self.with_properties(exp.Properties(expressions=with_properties))
     938
     939    def root_properties(self, properties: exp.Properties) -> str:
     940        if properties.expressions:
     941            return self.sep() + self.expressions(properties, indent=False, sep=" ")
     942        return ""
     943
     944    def properties(
     945        self,
     946        properties: exp.Properties,
     947        prefix: str = "",
     948        sep: str = ", ",
     949        suffix: str = "",
     950        wrapped: bool = True,
     951    ) -> str:
     952        if properties.expressions:
     953            expressions = self.expressions(properties, sep=sep, indent=False)
     954            expressions = self.wrap(expressions) if wrapped else expressions
     955            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
     956        return ""
     957
     958    def with_properties(self, properties: exp.Properties) -> str:
     959        return self.properties(properties, prefix=self.seg("WITH"))
     960
     961    def locate_properties(
     962        self, properties: exp.Properties
     963    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
     964        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
     965            key: [] for key in exp.Properties.Location
     966        }
     967
     968        for p in properties.expressions:
     969            p_loc = self.PROPERTIES_LOCATION[p.__class__]
     970            if p_loc == exp.Properties.Location.POST_NAME:
     971                properties_locs[exp.Properties.Location.POST_NAME].append(p)
     972            elif p_loc == exp.Properties.Location.POST_INDEX:
     973                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
     974            elif p_loc == exp.Properties.Location.POST_SCHEMA:
     975                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
     976            elif p_loc == exp.Properties.Location.POST_WITH:
     977                properties_locs[exp.Properties.Location.POST_WITH].append(p)
     978            elif p_loc == exp.Properties.Location.POST_CREATE:
     979                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
     980            elif p_loc == exp.Properties.Location.POST_ALIAS:
     981                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
     982            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
     983                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
     984            elif p_loc == exp.Properties.Location.UNSUPPORTED:
     985                self.unsupported(f"Unsupported property {p.key}")
     986
     987        return properties_locs
     988
     989    def property_sql(self, expression: exp.Property) -> str:
     990        property_cls = expression.__class__
     991        if property_cls == exp.Property:
     992            return f"{expression.name}={self.sql(expression, 'value')}"
     993
     994        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
     995        if not property_name:
     996            self.unsupported(f"Unsupported property {expression.key}")
     997
     998        return f"{property_name}={self.sql(expression, 'this')}"
     999
    1000    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1001        options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1002        options = f" {options}" if options else ""
    1003        return f"LIKE {self.sql(expression, 'this')}{options}"
    1004
    1005    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1006        no = "NO " if expression.args.get("no") else ""
    1007        protection = " PROTECTION" if expression.args.get("protection") else ""
    1008        return f"{no}FALLBACK{protection}"
    1009
    1010    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1011        no = "NO " if expression.args.get("no") else ""
    1012        local = expression.args.get("local")
    1013        local = f"{local} " if local else ""
    1014        dual = "DUAL " if expression.args.get("dual") else ""
    1015        before = "BEFORE " if expression.args.get("before") else ""
    1016        after = "AFTER " if expression.args.get("after") else ""
    1017        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1018
    1019    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1020        freespace = self.sql(expression, "this")
    1021        percent = " PERCENT" if expression.args.get("percent") else ""
    1022        return f"FREESPACE={freespace}{percent}"
    1023
    1024    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1025        if expression.args.get("default"):
    1026            property = "DEFAULT"
    1027        elif expression.args.get("on"):
    1028            property = "ON"
    1029        else:
    1030            property = "OFF"
    1031        return f"CHECKSUM={property}"
    1032
    1033    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1034        if expression.args.get("no"):
    1035            return "NO MERGEBLOCKRATIO"
    1036        if expression.args.get("default"):
    1037            return "DEFAULT MERGEBLOCKRATIO"
    1038
    1039        percent = " PERCENT" if expression.args.get("percent") else ""
    1040        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1041
    1042    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1043        default = expression.args.get("default")
    1044        minimum = expression.args.get("minimum")
    1045        maximum = expression.args.get("maximum")
    1046        if default or minimum or maximum:
    1047            if default:
    1048                prop = "DEFAULT"
    1049            elif minimum:
    1050                prop = "MINIMUM"
    1051            else:
    1052                prop = "MAXIMUM"
    1053            return f"{prop} DATABLOCKSIZE"
    1054        units = expression.args.get("units")
    1055        units = f" {units}" if units else ""
    1056        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1057
    1058    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1059        autotemp = expression.args.get("autotemp")
    1060        always = expression.args.get("always")
    1061        default = expression.args.get("default")
    1062        manual = expression.args.get("manual")
    1063        never = expression.args.get("never")
    1064
    1065        if autotemp is not None:
    1066            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1067        elif always:
    1068            prop = "ALWAYS"
    1069        elif default:
    1070            prop = "DEFAULT"
    1071        elif manual:
    1072            prop = "MANUAL"
    1073        elif never:
    1074            prop = "NEVER"
    1075        return f"BLOCKCOMPRESSION={prop}"
    1076
    1077    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1078        no = expression.args.get("no")
    1079        no = " NO" if no else ""
    1080        concurrent = expression.args.get("concurrent")
    1081        concurrent = " CONCURRENT" if concurrent else ""
    1082
    1083        for_ = ""
    1084        if expression.args.get("for_all"):
    1085            for_ = " FOR ALL"
    1086        elif expression.args.get("for_insert"):
    1087            for_ = " FOR INSERT"
    1088        elif expression.args.get("for_none"):
    1089            for_ = " FOR NONE"
    1090        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
    1091
    1092    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1093        kind = expression.args.get("kind")
    1094        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1095        for_or_in = expression.args.get("for_or_in")
    1096        lock_type = expression.args.get("lock_type")
    1097        override = " OVERRIDE" if expression.args.get("override") else ""
    1098        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"
    1099
    1100    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1101        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1102        statistics = expression.args.get("statistics")
    1103        statistics_sql = ""
    1104        if statistics is not None:
    1105            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1106        return f"{data_sql}{statistics_sql}"
    1107
    1108    def insert_sql(self, expression: exp.Insert) -> str:
    1109        overwrite = expression.args.get("overwrite")
    1110
    1111        if isinstance(expression.this, exp.Directory):
    1112            this = "OVERWRITE " if overwrite else "INTO "
    1113        else:
    1114            this = "OVERWRITE TABLE " if overwrite else "INTO "
    1115
    1116        alternative = expression.args.get("alternative")
    1117        alternative = f" OR {alternative} " if alternative else " "
    1118        this = f"{this}{self.sql(expression, 'this')}"
    1119
    1120        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1121        partition_sql = (
    1122            self.sql(expression, "partition") if expression.args.get("partition") else ""
    1123        )
    1124        expression_sql = self.sql(expression, "expression")
    1125        conflict = self.sql(expression, "conflict")
    1126        returning = self.sql(expression, "returning")
    1127        sep = self.sep() if partition_sql else ""
    1128        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{conflict}{returning}"
    1129        return self.prepend_ctes(expression, sql)
    1130
    1131    def intersect_sql(self, expression: exp.Intersect) -> str:
    1132        return self.prepend_ctes(
    1133            expression,
    1134            self.set_operation(expression, self.intersect_op(expression)),
    1135        )
    1136
    1137    def intersect_op(self, expression: exp.Intersect) -> str:
    1138        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
    1139
    1140    def introducer_sql(self, expression: exp.Introducer) -> str:
    1141        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1142
    1143    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1144        return expression.name.upper()
    1145
    1146    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1147        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1148        constraint = self.sql(expression, "constraint")
    1149        if constraint:
    1150            constraint = f"ON CONSTRAINT {constraint}"
    1151        key = self.expressions(expression, key="key", flat=True)
    1152        do = "" if expression.args.get("duplicate") else " DO "
    1153        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1154        expressions = self.expressions(expression, flat=True)
    1155        if expressions:
    1156            expressions = f"UPDATE SET {expressions}"
    1157        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
    1158
    1159    def returning_sql(self, expression: exp.Returning) -> str:
    1160        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1161
    1162    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1163        fields = expression.args.get("fields")
    1164        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1165        escaped = expression.args.get("escaped")
    1166        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1167        items = expression.args.get("collection_items")
    1168        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1169        keys = expression.args.get("map_keys")
    1170        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1171        lines = expression.args.get("lines")
    1172        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1173        null = expression.args.get("null")
    1174        null = f" NULL DEFINED AS {null}" if null else ""
    1175        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1176
    1177    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1178        table = ".".join(
    1179            part
    1180            for part in [
    1181                self.sql(expression, "catalog"),
    1182                self.sql(expression, "db"),
    1183                self.sql(expression, "this"),
    1184            ]
    1185            if part
    1186        )
    1187
    1188        alias = self.sql(expression, "alias")
    1189        alias = f"{sep}{alias}" if alias else ""
    1190        hints = self.expressions(expression, key="hints", flat=True)
    1191        hints = f" WITH ({hints})" if hints and self.TABLE_HINTS else ""
    1192        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1193        pivots = f" {pivots}" if pivots else ""
    1194        joins = self.expressions(expression, key="joins", sep="")
    1195        laterals = self.expressions(expression, key="laterals", sep="")
    1196        system_time = expression.args.get("system_time")
    1197        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
    1198
    1199        return f"{table}{system_time}{alias}{hints}{pivots}{joins}{laterals}"
    1200
    1201    def tablesample_sql(
    1202        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1203    ) -> str:
    1204        if self.alias_post_tablesample and expression.this.alias:
    1205            table = expression.this.copy()
    1206            table.set("alias", None)
    1207            this = self.sql(table)
    1208            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1209        else:
    1210            this = self.sql(expression, "this")
    1211            alias = ""
    1212        method = self.sql(expression, "method")
    1213        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1214        numerator = self.sql(expression, "bucket_numerator")
    1215        denominator = self.sql(expression, "bucket_denominator")
    1216        field = self.sql(expression, "bucket_field")
    1217        field = f" ON {field}" if field else ""
    1218        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1219        percent = self.sql(expression, "percent")
    1220        percent = f"{percent} PERCENT" if percent else ""
    1221        rows = self.sql(expression, "rows")
    1222        rows = f"{rows} ROWS" if rows else ""
    1223        size = self.sql(expression, "size")
    1224        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1225            size = f"{size} PERCENT"
    1226        seed = self.sql(expression, "seed")
    1227        seed = f" {seed_prefix} ({seed})" if seed else ""
    1228        kind = expression.args.get("kind", "TABLESAMPLE")
    1229        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"
    1230
    1231    def pivot_sql(self, expression: exp.Pivot) -> str:
    1232        expressions = self.expressions(expression, flat=True)
    1233
    1234        if expression.this:
    1235            this = self.sql(expression, "this")
    1236            on = f"{self.seg('ON')} {expressions}"
    1237            using = self.expressions(expression, key="using", flat=True)
    1238            using = f"{self.seg('USING')} {using}" if using else ""
    1239            group = self.sql(expression, "group")
    1240            return f"PIVOT {this}{on}{using}{group}"
    1241
    1242        alias = self.sql(expression, "alias")
    1243        alias = f" AS {alias}" if alias else ""
    1244        unpivot = expression.args.get("unpivot")
    1245        direction = "UNPIVOT" if unpivot else "PIVOT"
    1246        field = self.sql(expression, "field")
    1247        return f"{direction}({expressions} FOR {field}){alias}"
    1248
    1249    def tuple_sql(self, expression: exp.Tuple) -> str:
    1250        return f"({self.expressions(expression, flat=True)})"
    1251
    1252    def update_sql(self, expression: exp.Update) -> str:
    1253        this = self.sql(expression, "this")
    1254        set_sql = self.expressions(expression, flat=True)
    1255        from_sql = self.sql(expression, "from")
    1256        where_sql = self.sql(expression, "where")
    1257        returning = self.sql(expression, "returning")
    1258        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
    1259        return self.prepend_ctes(expression, sql)
    1260
    1261    def values_sql(self, expression: exp.Values) -> str:
    1262        args = self.expressions(expression)
    1263        alias = self.sql(expression, "alias")
    1264        values = f"VALUES{self.seg('')}{args}"
    1265        values = (
    1266            f"({values})"
    1267            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1268            else values
    1269        )
    1270        return f"{values} AS {alias}" if alias else values
    1271
    1272    def var_sql(self, expression: exp.Var) -> str:
    1273        return self.sql(expression, "this")
    1274
    1275    def into_sql(self, expression: exp.Into) -> str:
    1276        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1277        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    1278        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    1279
    1280    def from_sql(self, expression: exp.From) -> str:
    1281        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    1282
    1283    def group_sql(self, expression: exp.Group) -> str:
    1284        group_by = self.op_expressions("GROUP BY", expression)
    1285        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1286        grouping_sets = (
    1287            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1288        )
    1289
    1290        cube = expression.args.get("cube", [])
    1291        if seq_get(cube, 0) is True:
    1292            return f"{group_by}{self.seg('WITH CUBE')}"
    1293        else:
    1294            cube_sql = self.expressions(expression, key="cube", indent=False)
    1295            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1296
    1297        rollup = expression.args.get("rollup", [])
    1298        if seq_get(rollup, 0) is True:
    1299            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1300        else:
    1301            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1302            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1303
    1304        groupings = csv(
    1305            grouping_sets,
    1306            cube_sql,
    1307            rollup_sql,
    1308            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1309            sep=self.GROUPINGS_SEP,
    1310        )
    1311
    1312        if expression.args.get("expressions") and groupings:
    1313            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1314
    1315        return f"{group_by}{groupings}"
    1316
    1317    def having_sql(self, expression: exp.Having) -> str:
    1318        this = self.indent(self.sql(expression, "this"))
    1319        return f"{self.seg('HAVING')}{self.sep()}{this}"
    1320
    1321    def join_sql(self, expression: exp.Join) -> str:
    1322        op_sql = " ".join(
    1323            op
    1324            for op in (
    1325                expression.method,
    1326                "GLOBAL" if expression.args.get("global") else None,
    1327                expression.side,
    1328                expression.kind,
    1329                expression.hint if self.JOIN_HINTS else None,
    1330            )
    1331            if op
    1332        )
    1333        on_sql = self.sql(expression, "on")
    1334        using = expression.args.get("using")
    1335
    1336        if not on_sql and using:
    1337            on_sql = csv(*(self.sql(column) for column in using))
    1338
    1339        this_sql = self.sql(expression, "this")
    1340
    1341        if on_sql:
    1342            on_sql = self.indent(on_sql, skip_first=True)
    1343            space = self.seg(" " * self.pad) if self.pretty else " "
    1344            if using:
    1345                on_sql = f"{space}USING ({on_sql})"
    1346            else:
    1347                on_sql = f"{space}ON {on_sql}"
    1348        elif not op_sql:
    1349            return f", {this_sql}"
    1350
    1351        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1352        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
    1353
    1354    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    1355        args = self.expressions(expression, flat=True)
    1356        args = f"({args})" if len(args.split(",")) > 1 else args
    1357        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    1358
    1359    def lateral_sql(self, expression: exp.Lateral) -> str:
    1360        this = self.sql(expression, "this")
    1361
    1362        if isinstance(expression.this, exp.Subquery):
    1363            return f"LATERAL {this}"
    1364
    1365        if expression.args.get("view"):
    1366            alias = expression.args["alias"]
    1367            columns = self.expressions(alias, key="columns", flat=True)
    1368            table = f" {alias.name}" if alias.name else ""
    1369            columns = f" AS {columns}" if columns else ""
    1370            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1371            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1372
    1373        alias = self.sql(expression, "alias")
    1374        alias = f" AS {alias}" if alias else ""
    1375        return f"LATERAL {this}{alias}"
    1376
    1377    def limit_sql(self, expression: exp.Limit) -> str:
    1378        this = self.sql(expression, "this")
    1379        return f"{this}{self.seg('LIMIT')} {self.sql(expression, 'expression')}"
    1380
    1381    def offset_sql(self, expression: exp.Offset) -> str:
    1382        this = self.sql(expression, "this")
    1383        return f"{this}{self.seg('OFFSET')} {self.sql(expression, 'expression')}"
    1384
    1385    def setitem_sql(self, expression: exp.SetItem) -> str:
    1386        kind = self.sql(expression, "kind")
    1387        kind = f"{kind} " if kind else ""
    1388        this = self.sql(expression, "this")
    1389        expressions = self.expressions(expression)
    1390        collate = self.sql(expression, "collate")
    1391        collate = f" COLLATE {collate}" if collate else ""
    1392        global_ = "GLOBAL " if expression.args.get("global") else ""
    1393        return f"{global_}{kind}{this}{expressions}{collate}"
    1394
    1395    def set_sql(self, expression: exp.Set) -> str:
    1396        expressions = (
    1397            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1398        )
    1399        return f"SET{expressions}"
    1400
    1401    def pragma_sql(self, expression: exp.Pragma) -> str:
    1402        return f"PRAGMA {self.sql(expression, 'this')}"
    1403
    1404    def lock_sql(self, expression: exp.Lock) -> str:
    1405        if not self.LOCKING_READS_SUPPORTED:
    1406            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1407            return ""
    1408
    1409        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1410        expressions = self.expressions(expression, flat=True)
    1411        expressions = f" OF {expressions}" if expressions else ""
    1412        wait = expression.args.get("wait")
    1413
    1414        if wait is not None:
    1415            if isinstance(wait, exp.Literal):
    1416                wait = f" WAIT {self.sql(wait)}"
    1417            else:
    1418                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1419
    1420        return f"{lock_type}{expressions}{wait or ''}"
    1421
    1422    def literal_sql(self, expression: exp.Literal) -> str:
    1423        text = expression.this or ""
    1424        if expression.is_string:
    1425            text = text.replace(self.quote_end, self._escaped_quote_end)
    1426            if self.pretty:
    1427                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1428            text = f"{self.quote_start}{text}{self.quote_end}"
    1429        return text
    1430
    1431    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1432        local = " LOCAL" if expression.args.get("local") else ""
    1433        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1434        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1435        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1436        partition = self.sql(expression, "partition")
    1437        partition = f" {partition}" if partition else ""
    1438        input_format = self.sql(expression, "input_format")
    1439        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1440        serde = self.sql(expression, "serde")
    1441        serde = f" SERDE {serde}" if serde else ""
    1442        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    1443
    1444    def null_sql(self, *_) -> str:
    1445        return "NULL"
    1446
    1447    def boolean_sql(self, expression: exp.Boolean) -> str:
    1448        return "TRUE" if expression.this else "FALSE"
    1449
    1450    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1451        this = self.sql(expression, "this")
    1452        this = f"{this} " if this else this
    1453        return self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat)  # type: ignore
    1454
    1455    def cluster_sql(self, expression: exp.Cluster) -> str:
    1456        return self.op_expressions("CLUSTER BY", expression)
    1457
    1458    def distribute_sql(self, expression: exp.Distribute) -> str:
    1459        return self.op_expressions("DISTRIBUTE BY", expression)
    1460
    1461    def sort_sql(self, expression: exp.Sort) -> str:
    1462        return self.op_expressions("SORT BY", expression)
    1463
    1464    def ordered_sql(self, expression: exp.Ordered) -> str:
    1465        desc = expression.args.get("desc")
    1466        asc = not desc
    1467
    1468        nulls_first = expression.args.get("nulls_first")
    1469        nulls_last = not nulls_first
    1470        nulls_are_large = self.null_ordering == "nulls_are_large"
    1471        nulls_are_small = self.null_ordering == "nulls_are_small"
    1472        nulls_are_last = self.null_ordering == "nulls_are_last"
    1473
    1474        sort_order = " DESC" if desc else ""
    1475        nulls_sort_change = ""
    1476        if nulls_first and (
    1477            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1478        ):
    1479            nulls_sort_change = " NULLS FIRST"
    1480        elif (
    1481            nulls_last
    1482            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1483            and not nulls_are_last
    1484        ):
    1485            nulls_sort_change = " NULLS LAST"
    1486
    1487        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1488            self.unsupported(
    1489                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1490            )
    1491            nulls_sort_change = ""
    1492
    1493        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"
    1494
    1495    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1496        partition = self.partition_by_sql(expression)
    1497        order = self.sql(expression, "order")
    1498        measures = self.expressions(expression, key="measures")
    1499        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1500        rows = self.sql(expression, "rows")
    1501        rows = self.seg(rows) if rows else ""
    1502        after = self.sql(expression, "after")
    1503        after = self.seg(after) if after else ""
    1504        pattern = self.sql(expression, "pattern")
    1505        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1506        definition_sqls = [
    1507            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1508            for definition in expression.args.get("define", [])
    1509        ]
    1510        definitions = self.expressions(sqls=definition_sqls)
    1511        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1512        body = "".join(
    1513            (
    1514                partition,
    1515                order,
    1516                measures,
    1517                rows,
    1518                after,
    1519                pattern,
    1520                define,
    1521            )
    1522        )
    1523        alias = self.sql(expression, "alias")
    1524        alias = f" {alias}" if alias else ""
    1525        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    1526
    1527    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1528        limit = expression.args.get("limit")
    1529
    1530        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1531            limit = exp.Limit(expression=limit.args.get("count"))
    1532        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1533            limit = exp.Fetch(direction="FIRST", count=limit.expression)
    1534
    1535        fetch = isinstance(limit, exp.Fetch)
    1536
    1537        return csv(
    1538            *sqls,
    1539            *[self.sql(join) for join in expression.args.get("joins") or []],
    1540            self.sql(expression, "match"),
    1541            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1542            self.sql(expression, "where"),
    1543            self.sql(expression, "group"),
    1544            self.sql(expression, "having"),
    1545            *self.after_having_modifiers(expression),
    1546            self.sql(expression, "order"),
    1547            self.sql(expression, "offset") if fetch else self.sql(limit),
    1548            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1549            *self.after_limit_modifiers(expression),
    1550            sep="",
    1551        )
    1552
    1553    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1554        return [
    1555            self.sql(expression, "qualify"),
    1556            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1557            if expression.args.get("windows")
    1558            else "",
    1559        ]
    1560
    1561    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1562        locks = self.expressions(expression, key="locks", sep=" ")
    1563        locks = f" {locks}" if locks else ""
    1564        return [locks, self.sql(expression, "sample")]
    1565
    1566    def select_sql(self, expression: exp.Select) -> str:
    1567        hint = self.sql(expression, "hint")
    1568        distinct = self.sql(expression, "distinct")
    1569        distinct = f" {distinct}" if distinct else ""
    1570        kind = expression.args.get("kind")
    1571        kind = f" AS {kind}" if kind else ""
    1572        expressions = self.expressions(expression)
    1573        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1574        sql = self.query_modifiers(
    1575            expression,
    1576            f"SELECT{hint}{distinct}{kind}{expressions}",
    1577            self.sql(expression, "into", comment=False),
    1578            self.sql(expression, "from", comment=False),
    1579        )
    1580        return self.prepend_ctes(expression, sql)
    1581
    1582    def schema_sql(self, expression: exp.Schema) -> str:
    1583        this = self.sql(expression, "this")
    1584        this = f"{this} " if this else ""
    1585        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1586        return f"{this}{sql}"
    1587
    1588    def star_sql(self, expression: exp.Star) -> str:
    1589        except_ = self.expressions(expression, key="except", flat=True)
    1590        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1591        replace = self.expressions(expression, key="replace", flat=True)
    1592        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1593        return f"*{except_}{replace}"
    1594
    1595    def parameter_sql(self, expression: exp.Parameter) -> str:
    1596        this = self.sql(expression, "this")
    1597        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1598        return f"{self.PARAMETER_TOKEN}{this}"
    1599
    1600    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1601        this = self.sql(expression, "this")
    1602        kind = expression.text("kind")
    1603        if kind:
    1604            kind = f"{kind}."
    1605        return f"@@{kind}{this}"
    1606
    1607    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    1608        return f":{expression.name}" if expression.name else "?"
    1609
    1610    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1611        alias = self.sql(expression, "alias")
    1612        alias = f"{sep}{alias}" if alias else ""
    1613
    1614        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1615        pivots = f" {pivots}" if pivots else ""
    1616
    1617        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    1618        return self.prepend_ctes(expression, sql)
    1619
    1620    def qualify_sql(self, expression: exp.Qualify) -> str:
    1621        this = self.indent(self.sql(expression, "this"))
    1622        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    1623
    1624    def union_sql(self, expression: exp.Union) -> str:
    1625        return self.prepend_ctes(
    1626            expression,
    1627            self.set_operation(expression, self.union_op(expression)),
    1628        )
    1629
    1630    def union_op(self, expression: exp.Union) -> str:
    1631        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1632        kind = kind if expression.args.get("distinct") else " ALL"
    1633        return f"UNION{kind}"
    1634
    1635    def unnest_sql(self, expression: exp.Unnest) -> str:
    1636        args = self.expressions(expression, flat=True)
    1637        alias = expression.args.get("alias")
    1638        if alias and self.unnest_column_only:
    1639            columns = alias.columns
    1640            alias = self.sql(columns[0]) if columns else ""
    1641        else:
    1642            alias = self.sql(expression, "alias")
    1643        alias = f" AS {alias}" if alias else alias
    1644        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1645        offset = expression.args.get("offset")
    1646        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1647        return f"UNNEST({args}){ordinality}{alias}{offset}"
    1648
    1649    def where_sql(self, expression: exp.Where) -> str:
    1650        this = self.indent(self.sql(expression, "this"))
    1651        return f"{self.seg('WHERE')}{self.sep()}{this}"
    1652
    1653    def window_sql(self, expression: exp.Window) -> str:
    1654        this = self.sql(expression, "this")
    1655        partition = self.partition_by_sql(expression)
    1656        order = expression.args.get("order")
    1657        order = self.order_sql(order, flat=True) if order else ""
    1658        spec = self.sql(expression, "spec")
    1659        alias = self.sql(expression, "alias")
    1660        over = self.sql(expression, "over") or "OVER"
    1661
    1662        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    1663
    1664        first = expression.args.get("first")
    1665        if first is None:
    1666            first = ""
    1667        else:
    1668            first = "FIRST" if first else "LAST"
    1669
    1670        if not partition and not order and not spec and alias:
    1671            return f"{this} {alias}"
    1672
    1673        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    1674        return f"{this} ({args})"
    1675
    1676    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    1677        partition = self.expressions(expression, key="partition_by", flat=True)
    1678        return f"PARTITION BY {partition}" if partition else ""
    1679
    1680    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    1681        kind = self.sql(expression, "kind")
    1682        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1683        end = (
    1684            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1685            or "CURRENT ROW"
    1686        )
    1687        return f"{kind} BETWEEN {start} AND {end}"
    1688
    1689    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    1690        this = self.sql(expression, "this")
    1691        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    1692        return f"{this} WITHIN GROUP ({expression_sql})"
    1693
    1694    def between_sql(self, expression: exp.Between) -> str:
    1695        this = self.sql(expression, "this")
    1696        low = self.sql(expression, "low")
    1697        high = self.sql(expression, "high")
    1698        return f"{this} BETWEEN {low} AND {high}"
    1699
    1700    def bracket_sql(self, expression: exp.Bracket) -> str:
    1701        expressions = apply_index_offset(expression.this, expression.expressions, self.index_offset)
    1702        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1703
    1704        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    1705
    1706    def all_sql(self, expression: exp.All) -> str:
    1707        return f"ALL {self.wrap(expression)}"
    1708
    1709    def any_sql(self, expression: exp.Any) -> str:
    1710        this = self.sql(expression, "this")
    1711        if isinstance(expression.this, exp.Subqueryable):
    1712            this = self.wrap(this)
    1713        return f"ANY {this}"
    1714
    1715    def exists_sql(self, expression: exp.Exists) -> str:
    1716        return f"EXISTS{self.wrap(expression)}"
    1717
    1718    def case_sql(self, expression: exp.Case) -> str:
    1719        this = self.sql(expression, "this")
    1720        statements = [f"CASE {this}" if this else "CASE"]
    1721
    1722        for e in expression.args["ifs"]:
    1723            statements.append(f"WHEN {self.sql(e, 'this')}")
    1724            statements.append(f"THEN {self.sql(e, 'true')}")
    1725
    1726        default = self.sql(expression, "default")
    1727
    1728        if default:
    1729            statements.append(f"ELSE {default}")
    1730
    1731        statements.append("END")
    1732
    1733        if self.pretty and self.text_width(statements) > self._max_text_width:
    1734            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1735
    1736        return " ".join(statements)
    1737
    1738    def constraint_sql(self, expression: exp.Constraint) -> str:
    1739        this = self.sql(expression, "this")
    1740        expressions = self.expressions(expression, flat=True)
    1741        return f"CONSTRAINT {this} {expressions}"
    1742
    1743    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    1744        order = expression.args.get("order")
    1745        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    1746        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    1747
    1748    def extract_sql(self, expression: exp.Extract) -> str:
    1749        this = self.sql(expression, "this")
    1750        expression_sql = self.sql(expression, "expression")
    1751        return f"EXTRACT({this} FROM {expression_sql})"
    1752
    1753    def trim_sql(self, expression: exp.Trim) -> str:
    1754        trim_type = self.sql(expression, "position")
    1755
    1756        if trim_type == "LEADING":
    1757            return self.func("LTRIM", expression.this)
    1758        elif trim_type == "TRAILING":
    1759            return self.func("RTRIM", expression.this)
    1760        else:
    1761            return self.func("TRIM", expression.this, expression.expression)
    1762
    1763    def concat_sql(self, expression: exp.Concat) -> str:
    1764        if len(expression.expressions) == 1:
    1765            return self.sql(expression.expressions[0])
    1766        return self.function_fallback_sql(expression)
    1767
    1768    def check_sql(self, expression: exp.Check) -> str:
    1769        this = self.sql(expression, key="this")
    1770        return f"CHECK ({this})"
    1771
    1772    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1773        expressions = self.expressions(expression, flat=True)
    1774        reference = self.sql(expression, "reference")
    1775        reference = f" {reference}" if reference else ""
    1776        delete = self.sql(expression, "delete")
    1777        delete = f" ON DELETE {delete}" if delete else ""
    1778        update = self.sql(expression, "update")
    1779        update = f" ON UPDATE {update}" if update else ""
    1780        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
    1781
    1782    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1783        expressions = self.expressions(expression, flat=True)
    1784        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1785        options = f" {options}" if options else ""
    1786        return f"PRIMARY KEY ({expressions}){options}"
    1787
    1788    def if_sql(self, expression: exp.If) -> str:
    1789        return self.case_sql(
    1790            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1791        )
    1792
    1793    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    1794        modifier = expression.args.get("modifier")
    1795        modifier = f" {modifier}" if modifier else ""
    1796        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    1797
    1798    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    1799        return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}"
    1800
    1801    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    1802        expressions = self.expressions(expression)
    1803        null_handling = expression.args.get("null_handling")
    1804        null_handling = f" {null_handling}" if null_handling else ""
    1805        unique_keys = expression.args.get("unique_keys")
    1806        if unique_keys is not None:
    1807            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    1808        else:
    1809            unique_keys = ""
    1810        return_type = self.sql(expression, "return_type")
    1811        return_type = f" RETURNING {return_type}" if return_type else ""
    1812        format_json = " FORMAT JSON" if expression.args.get("format_json") else ""
    1813        encoding = self.sql(expression, "encoding")
    1814        encoding = f" ENCODING {encoding}" if encoding else ""
    1815        return f"JSON_OBJECT({expressions}{null_handling}{unique_keys}{return_type}{format_json}{encoding})"
    1816
    1817    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    1818        this = self.sql(expression, "this")
    1819        kind = self.sql(expression, "kind")
    1820        path = self.sql(expression, "path")
    1821        path = f" {path}" if path else ""
    1822        as_json = " AS JSON" if expression.args.get("as_json") else ""
    1823        return f"{this} {kind}{path}{as_json}"
    1824
    1825    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    1826        this = self.sql(expression, "this")
    1827        path = self.sql(expression, "path")
    1828        path = f", {path}" if path else ""
    1829        expressions = self.expressions(expression)
    1830        with_ = (
    1831            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    1832            if expressions
    1833            else ""
    1834        )
    1835        return f"OPENJSON({this}{path}){with_}"
    1836
    1837    def in_sql(self, expression: exp.In) -> str:
    1838        query = expression.args.get("query")
    1839        unnest = expression.args.get("unnest")
    1840        field = expression.args.get("field")
    1841        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1842
    1843        if query:
    1844            in_sql = self.wrap(query)
    1845        elif unnest:
    1846            in_sql = self.in_unnest_op(unnest)
    1847        elif field:
    1848            in_sql = self.sql(field)
    1849        else:
    1850            in_sql = f"({self.expressions(expression, flat=True)})"
    1851
    1852        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    1853
    1854    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    1855        return f"(SELECT {self.sql(unnest)})"
    1856
    1857    def interval_sql(self, expression: exp.Interval) -> str:
    1858        unit = self.sql(expression, "unit")
    1859        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    1860            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    1861        unit = f" {unit}" if unit else ""
    1862
    1863        if self.SINGLE_STRING_INTERVAL:
    1864            this = expression.this.name if expression.this else ""
    1865            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    1866
    1867        this = self.sql(expression, "this")
    1868        if this:
    1869            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    1870            this = f" {this}" if unwrapped else f" ({this})"
    1871
    1872        return f"INTERVAL{this}{unit}"
    1873
    1874    def return_sql(self, expression: exp.Return) -> str:
    1875        return f"RETURN {self.sql(expression, 'this')}"
    1876
    1877    def reference_sql(self, expression: exp.Reference) -> str:
    1878        this = self.sql(expression, "this")
    1879        expressions = self.expressions(expression, flat=True)
    1880        expressions = f"({expressions})" if expressions else ""
    1881        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1882        options = f" {options}" if options else ""
    1883        return f"REFERENCES {this}{expressions}{options}"
    1884
    1885    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    1886        return self.func(expression.name, *expression.expressions)
    1887
    1888    def paren_sql(self, expression: exp.Paren) -> str:
    1889        if isinstance(expression.unnest(), exp.Select):
    1890            sql = self.wrap(expression)
    1891        else:
    1892            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1893            sql = f"({sql}{self.seg(')', sep='')}"
    1894
    1895        return self.prepend_ctes(expression, sql)
    1896
    1897    def neg_sql(self, expression: exp.Neg) -> str:
    1898        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    1899        this_sql = self.sql(expression, "this")
    1900        sep = " " if this_sql[0] == "-" else ""
    1901        return f"-{sep}{this_sql}"
    1902
    1903    def not_sql(self, expression: exp.Not) -> str:
    1904        return f"NOT {self.sql(expression, 'this')}"
    1905
    1906    def alias_sql(self, expression: exp.Alias) -> str:
    1907        alias = self.sql(expression, "alias")
    1908        alias = f" AS {alias}" if alias else ""
    1909        return f"{self.sql(expression, 'this')}{alias}"
    1910
    1911    def aliases_sql(self, expression: exp.Aliases) -> str:
    1912        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    1913
    1914    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1915        this = self.sql(expression, "this")
    1916        zone = self.sql(expression, "zone")
    1917        return f"{this} AT TIME ZONE {zone}"
    1918
    1919    def add_sql(self, expression: exp.Add) -> str:
    1920        return self.binary(expression, "+")
    1921
    1922    def and_sql(self, expression: exp.And) -> str:
    1923        return self.connector_sql(expression, "AND")
    1924
    1925    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1926        if not self.pretty:
    1927            return self.binary(expression, op)
    1928
    1929        sqls = tuple(
    1930            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    1931            for i, e in enumerate(expression.flatten(unnest=False))
    1932        )
    1933
    1934        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1935        return f"{sep}{op} ".join(sqls)
    1936
    1937    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    1938        return self.binary(expression, "&")
    1939
    1940    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    1941        return self.binary(expression, "<<")
    1942
    1943    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    1944        return f"~{self.sql(expression, 'this')}"
    1945
    1946    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    1947        return self.binary(expression, "|")
    1948
    1949    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    1950        return self.binary(expression, ">>")
    1951
    1952    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    1953        return self.binary(expression, "^")
    1954
    1955    def cast_sql(self, expression: exp.Cast) -> str:
    1956        return f"CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
    1957
    1958    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    1959        zone = self.sql(expression, "this")
    1960        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    1961
    1962    def collate_sql(self, expression: exp.Collate) -> str:
    1963        return self.binary(expression, "COLLATE")
    1964
    1965    def command_sql(self, expression: exp.Command) -> str:
    1966        return f"{self.sql(expression, 'this').upper()} {expression.text('expression').strip()}"
    1967
    1968    def comment_sql(self, expression: exp.Comment) -> str:
    1969        this = self.sql(expression, "this")
    1970        kind = expression.args["kind"]
    1971        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1972        expression_sql = self.sql(expression, "expression")
    1973        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
    1974
    1975    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    1976        this = self.sql(expression, "this")
    1977        delete = " DELETE" if expression.args.get("delete") else ""
    1978        recompress = self.sql(expression, "recompress")
    1979        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    1980        to_disk = self.sql(expression, "to_disk")
    1981        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    1982        to_volume = self.sql(expression, "to_volume")
    1983        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    1984        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    1985
    1986    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    1987        where = self.sql(expression, "where")
    1988        group = self.sql(expression, "group")
    1989        aggregates = self.expressions(expression, key="aggregates")
    1990        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    1991
    1992        if not (where or group or aggregates) and len(expression.expressions) == 1:
    1993            return f"TTL {self.expressions(expression, flat=True)}"
    1994
    1995        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    1996
    1997    def transaction_sql(self, expression: exp.Transaction) -> str:
    1998        return "BEGIN"
    1999
    2000    def commit_sql(self, expression: exp.Commit) -> str:
    2001        chain = expression.args.get("chain")
    2002        if chain is not None:
    2003            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2004
    2005        return f"COMMIT{chain or ''}"
    2006
    2007    def rollback_sql(self, expression: exp.Rollback) -> str:
    2008        savepoint = expression.args.get("savepoint")
    2009        savepoint = f" TO {savepoint}" if savepoint else ""
    2010        return f"ROLLBACK{savepoint}"
    2011
    2012    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2013        this = self.sql(expression, "this")
    2014
    2015        dtype = self.sql(expression, "dtype")
    2016        if dtype:
    2017            collate = self.sql(expression, "collate")
    2018            collate = f" COLLATE {collate}" if collate else ""
    2019            using = self.sql(expression, "using")
    2020            using = f" USING {using}" if using else ""
    2021            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    2022
    2023        default = self.sql(expression, "default")
    2024        if default:
    2025            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2026
    2027        if not expression.args.get("drop"):
    2028            self.unsupported("Unsupported ALTER COLUMN syntax")
    2029
    2030        return f"ALTER COLUMN {this} DROP DEFAULT"
    2031
    2032    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2033        if not self.RENAME_TABLE_WITH_DB:
    2034            # Remove db from tables
    2035            expression = expression.transform(
    2036                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2037            )
    2038        this = self.sql(expression, "this")
    2039        return f"RENAME TO {this}"
    2040
    2041    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2042        actions = expression.args["actions"]
    2043
    2044        if isinstance(actions[0], exp.ColumnDef):
    2045            actions = self.expressions(expression, key="actions", prefix="ADD COLUMN ")
    2046        elif isinstance(actions[0], exp.Schema):
    2047            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2048        elif isinstance(actions[0], exp.Delete):
    2049            actions = self.expressions(expression, key="actions", flat=True)
    2050        else:
    2051            actions = self.expressions(expression, key="actions")
    2052
    2053        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2054        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"
    2055
    2056    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2057        expressions = self.expressions(expression)
    2058        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2059        return f"DROP{exists}{expressions}"
    2060
    2061    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2062        this = self.sql(expression, "this")
    2063        expression_ = self.sql(expression, "expression")
    2064        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2065
    2066        enforced = expression.args.get("enforced")
    2067        if enforced is not None:
    2068            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2069
    2070        return f"{add_constraint} {expression_}"
    2071
    2072    def distinct_sql(self, expression: exp.Distinct) -> str:
    2073        this = self.expressions(expression, flat=True)
    2074        this = f" {this}" if this else ""
    2075
    2076        on = self.sql(expression, "on")
    2077        on = f" ON {on}" if on else ""
    2078        return f"DISTINCT{this}{on}"
    2079
    2080    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    2081        return f"{self.sql(expression, 'this')} IGNORE NULLS"
    2082
    2083    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    2084        return f"{self.sql(expression, 'this')} RESPECT NULLS"
    2085
    2086    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2087        return self.sql(
    2088            exp.Cast(
    2089                this=exp.Div(this=expression.this, expression=expression.expression),
    2090                to=exp.DataType(this=exp.DataType.Type.INT),
    2091            )
    2092        )
    2093
    2094    def dpipe_sql(self, expression: exp.DPipe) -> str:
    2095        return self.binary(expression, "||")
    2096
    2097    def div_sql(self, expression: exp.Div) -> str:
    2098        return self.binary(expression, "/")
    2099
    2100    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    2101        return self.binary(expression, "OVERLAPS")
    2102
    2103    def distance_sql(self, expression: exp.Distance) -> str:
    2104        return self.binary(expression, "<->")
    2105
    2106    def dot_sql(self, expression: exp.Dot) -> str:
    2107        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    2108
    2109    def eq_sql(self, expression: exp.EQ) -> str:
    2110        return self.binary(expression, "=")
    2111
    2112    def escape_sql(self, expression: exp.Escape) -> str:
    2113        return self.binary(expression, "ESCAPE")
    2114
    2115    def glob_sql(self, expression: exp.Glob) -> str:
    2116        return self.binary(expression, "GLOB")
    2117
    2118    def gt_sql(self, expression: exp.GT) -> str:
    2119        return self.binary(expression, ">")
    2120
    2121    def gte_sql(self, expression: exp.GTE) -> str:
    2122        return self.binary(expression, ">=")
    2123
    2124    def ilike_sql(self, expression: exp.ILike) -> str:
    2125        return self.binary(expression, "ILIKE")
    2126
    2127    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    2128        return self.binary(expression, "ILIKE ANY")
    2129
    2130    def is_sql(self, expression: exp.Is) -> str:
    2131        return self.binary(expression, "IS")
    2132
    2133    def like_sql(self, expression: exp.Like) -> str:
    2134        return self.binary(expression, "LIKE")
    2135
    2136    def likeany_sql(self, expression: exp.LikeAny) -> str:
    2137        return self.binary(expression, "LIKE ANY")
    2138
    2139    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    2140        return self.binary(expression, "SIMILAR TO")
    2141
    2142    def lt_sql(self, expression: exp.LT) -> str:
    2143        return self.binary(expression, "<")
    2144
    2145    def lte_sql(self, expression: exp.LTE) -> str:
    2146        return self.binary(expression, "<=")
    2147
    2148    def mod_sql(self, expression: exp.Mod) -> str:
    2149        return self.binary(expression, "%")
    2150
    2151    def mul_sql(self, expression: exp.Mul) -> str:
    2152        return self.binary(expression, "*")
    2153
    2154    def neq_sql(self, expression: exp.NEQ) -> str:
    2155        return self.binary(expression, "<>")
    2156
    2157    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    2158        return self.binary(expression, "IS NOT DISTINCT FROM")
    2159
    2160    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    2161        return self.binary(expression, "IS DISTINCT FROM")
    2162
    2163    def or_sql(self, expression: exp.Or) -> str:
    2164        return self.connector_sql(expression, "OR")
    2165
    2166    def slice_sql(self, expression: exp.Slice) -> str:
    2167        return self.binary(expression, ":")
    2168
    2169    def sub_sql(self, expression: exp.Sub) -> str:
    2170        return self.binary(expression, "-")
    2171
    2172    def trycast_sql(self, expression: exp.TryCast) -> str:
    2173        return f"TRY_CAST({self.sql(expression, 'this')} AS {self.sql(expression, 'to')})"
    2174
    2175    def use_sql(self, expression: exp.Use) -> str:
    2176        kind = self.sql(expression, "kind")
    2177        kind = f" {kind}" if kind else ""
    2178        this = self.sql(expression, "this")
    2179        this = f" {this}" if this else ""
    2180        return f"USE{kind}{this}"
    2181
    2182    def binary(self, expression: exp.Binary, op: str) -> str:
    2183        op = self.maybe_comment(op, comments=expression.comments)
    2184        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
    2185
    2186    def function_fallback_sql(self, expression: exp.Func) -> str:
    2187        args = []
    2188        for arg_value in expression.args.values():
    2189            if isinstance(arg_value, list):
    2190                for value in arg_value:
    2191                    args.append(value)
    2192            else:
    2193                args.append(arg_value)
    2194
    2195        return self.func(expression.sql_name(), *args)
    2196
    2197    def func(self, name: str, *args: t.Optional[exp.Expression | str]) -> str:
    2198        return f"{self.normalize_func(name)}({self.format_args(*args)})"
    2199
    2200    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    2201        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    2202        if self.pretty and self.text_width(arg_sqls) > self._max_text_width:
    2203            return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    2204        return ", ".join(arg_sqls)
    2205
    2206    def text_width(self, args: t.Iterable) -> int:
    2207        return sum(len(arg) for arg in args)
    2208
    2209    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2210        return format_time(self.sql(expression, "format"), self.time_mapping, self.time_trie)
    2211
    2212    def expressions(
    2213        self,
    2214        expression: t.Optional[exp.Expression] = None,
    2215        key: t.Optional[str] = None,
    2216        sqls: t.Optional[t.List[str]] = None,
    2217        flat: bool = False,
    2218        indent: bool = True,
    2219        sep: str = ", ",
    2220        prefix: str = "",
    2221    ) -> str:
    2222        expressions = expression.args.get(key or "expressions") if expression else sqls
    2223
    2224        if not expressions:
    2225            return ""
    2226
    2227        if flat:
    2228            return sep.join(self.sql(e) for e in expressions)
    2229
    2230        num_sqls = len(expressions)
    2231
    2232        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2233        pad = " " * self.pad
    2234        stripped_sep = sep.strip()
    2235
    2236        result_sqls = []
    2237        for i, e in enumerate(expressions):
    2238            sql = self.sql(e, comment=False)
    2239            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2240
    2241            if self.pretty:
    2242                if self._leading_comma:
    2243                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2244                else:
    2245                    result_sqls.append(
    2246                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2247                    )
    2248            else:
    2249                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2250
    2251        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2252        return self.indent(result_sql, skip_first=False) if indent else result_sql
    2253
    2254    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2255        flat = flat or isinstance(expression.parent, exp.Properties)
    2256        expressions_sql = self.expressions(expression, flat=flat)
    2257        if flat:
    2258            return f"{op} {expressions_sql}"
    2259        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    2260
    2261    def naked_property(self, expression: exp.Property) -> str:
    2262        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2263        if not property_name:
    2264            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2265        return f"{property_name} {self.sql(expression, 'this')}"
    2266
    2267    def set_operation(self, expression: exp.Expression, op: str) -> str:
    2268        this = self.sql(expression, "this")
    2269        op = self.seg(op)
    2270        return self.query_modifiers(
    2271            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2272        )
    2273
    2274    def tag_sql(self, expression: exp.Tag) -> str:
    2275        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    2276
    2277    def token_sql(self, token_type: TokenType) -> str:
    2278        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    2279
    2280    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2281        this = self.sql(expression, "this")
    2282        expressions = self.no_identify(self.expressions, expression)
    2283        expressions = (
    2284            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2285        )
    2286        return f"{this}{expressions}"
    2287
    2288    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2289        this = self.sql(expression, "this")
    2290        expressions = self.expressions(expression, flat=True)
    2291        return f"{this}({expressions})"
    2292
    2293    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    2294        return self.binary(expression, "=>")
    2295
    2296    def when_sql(self, expression: exp.When) -> str:
    2297        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2298        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2299        condition = self.sql(expression, "condition")
    2300        condition = f" AND {condition}" if condition else ""
    2301
    2302        then_expression = expression.args.get("then")
    2303        if isinstance(then_expression, exp.Insert):
    2304            then = f"INSERT {self.sql(then_expression, 'this')}"
    2305            if "expression" in then_expression.args:
    2306                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2307        elif isinstance(then_expression, exp.Update):
    2308            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2309                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2310            else:
    2311                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2312        else:
    2313            then = self.sql(then_expression)
    2314        return f"WHEN {matched}{source}{condition} THEN {then}"
    2315
    2316    def merge_sql(self, expression: exp.Merge) -> str:
    2317        this = self.sql(expression, "this")
    2318        using = f"USING {self.sql(expression, 'using')}"
    2319        on = f"ON {self.sql(expression, 'on')}"
    2320        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"
    2321
    2322    def tochar_sql(self, expression: exp.ToChar) -> str:
    2323        if expression.args.get("format"):
    2324            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2325
    2326        return self.sql(exp.cast(expression.this, "text"))
    2327
    2328    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2329        this = self.sql(expression, "this")
    2330        kind = self.sql(expression, "kind")
    2331        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2332        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2333        return f"{this}({kind}{args})"
    2334
    2335    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2336        this = self.sql(expression, "this")
    2337        max = self.sql(expression, "max")
    2338        min = self.sql(expression, "min")
    2339        return f"{this}(MIN {min} MAX {max})"
    2340
    2341    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    2342        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"

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

###### Arguments:

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

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

    287    def __init__(
    288        self,
    289        time_mapping=None,
    290        time_trie=None,
    291        pretty=None,
    292        quote_start=None,
    293        quote_end=None,
    294        identifier_start=None,
    295        identifier_end=None,
    296        bit_start=None,
    297        bit_end=None,
    298        hex_start=None,
    299        hex_end=None,
    300        byte_start=None,
    301        byte_end=None,
    302        raw_start=None,
    303        raw_end=None,
    304        identify=False,
    305        normalize=False,
    306        string_escape=None,
    307        identifier_escape=None,
    308        pad=2,
    309        indent=2,
    310        index_offset=0,
    311        unnest_column_only=False,
    312        alias_post_tablesample=False,
    313        identifiers_can_start_with_digit=False,
    314        normalize_functions="upper",
    315        unsupported_level=ErrorLevel.WARN,
    316        null_ordering=None,
    317        max_unsupported=3,
    318        leading_comma=False,
    319        max_text_width=80,
    320        comments=True,
    321    ):
    322        import sqlglot
    323
    324        self.time_mapping = time_mapping or {}
    325        self.time_trie = time_trie
    326        self.pretty = pretty if pretty is not None else sqlglot.pretty
    327        self.quote_start = quote_start or "'"
    328        self.quote_end = quote_end or "'"
    329        self.identifier_start = identifier_start or '"'
    330        self.identifier_end = identifier_end or '"'
    331        self.bit_start = bit_start
    332        self.bit_end = bit_end
    333        self.hex_start = hex_start
    334        self.hex_end = hex_end
    335        self.byte_start = byte_start
    336        self.byte_end = byte_end
    337        self.raw_start = raw_start
    338        self.raw_end = raw_end
    339        self.identify = identify
    340        self.normalize = normalize
    341        self.string_escape = string_escape or "'"
    342        self.identifier_escape = identifier_escape or '"'
    343        self.pad = pad
    344        self.index_offset = index_offset
    345        self.unnest_column_only = unnest_column_only
    346        self.alias_post_tablesample = alias_post_tablesample
    347        self.identifiers_can_start_with_digit = identifiers_can_start_with_digit
    348        self.normalize_functions = normalize_functions
    349        self.unsupported_level = unsupported_level
    350        self.unsupported_messages = []
    351        self.max_unsupported = max_unsupported
    352        self.null_ordering = null_ordering
    353        self._indent = indent
    354        self._escaped_quote_end = self.string_escape + self.quote_end
    355        self._escaped_identifier_end = self.identifier_escape + self.identifier_end
    356        self._leading_comma = leading_comma
    357        self._max_text_width = max_text_width
    358        self._comments = comments
    359        self._cache = None

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

    361    def generate(
    362        self,
    363        expression: t.Optional[exp.Expression],
    364        cache: t.Optional[t.Dict[int, str]] = None,
    365    ) -> str:
    366        """
    367        Generates a SQL string by interpreting the given syntax tree.
    368
    369        Args
    370            expression: the syntax tree.
    371            cache: an optional sql string cache. this leverages the hash of an expression which is slow, so only use this if you set _hash on each node.
    372
    373        Returns
    374            the SQL string.
    375        """
    376        if cache is not None:
    377            self._cache = cache
    378        self.unsupported_messages = []
    379        sql = self.sql(expression).strip()
    380        self._cache = None
    381
    382        if self.unsupported_level == ErrorLevel.IGNORE:
    383            return sql
    384
    385        if self.unsupported_level == ErrorLevel.WARN:
    386            for msg in self.unsupported_messages:
    387                logger.warning(msg)
    388        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    389            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    390
    391        if self.pretty:
    392            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    393        return sql

Generates a SQL string by interpreting the given syntax tree.

Args expression: the syntax tree. cache: an optional sql string cache. this leverages the hash of an expression which is slow, so only use this if you set _hash on each node.

Returns the SQL string.

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

    395    def unsupported(self, message: str) -> None:
    396        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    397            raise UnsupportedError(message)
    398        self.unsupported_messages.append(message)

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

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

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

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

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

    406    def pad_comment(self, comment: str) -> str:
    407        comment = " " + comment if comment[0].strip() else comment
    408        comment = comment + " " if comment[-1].strip() else comment
    409        return comment

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

    411    def maybe_comment(
    412        self,
    413        sql: str,
    414        expression: t.Optional[exp.Expression] = None,
    415        comments: t.Optional[t.List[str]] = None,
    416    ) -> str:
    417        comments = ((expression and expression.comments) if comments is None else comments) if self._comments else None  # type: ignore
    418
    419        if not comments or isinstance(expression, exp.Binary):
    420            return sql
    421
    422        sep = "\n" if self.pretty else " "
    423        comments_sql = sep.join(
    424            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    425        )
    426
    427        if not comments_sql:
    428            return sql
    429
    430        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    431            return (
    432                f"{self.sep()}{comments_sql}{sql}"
    433                if sql[0].isspace()
    434                else f"{comments_sql}{self.sep()}{sql}"
    435            )
    436
    437        return f"{sql} {comments_sql}"

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

    439    def wrap(self, expression: exp.Expression | str) -> str:
    440        this_sql = self.indent(
    441            self.sql(expression)
    442            if isinstance(expression, (exp.Select, exp.Union))
    443            else self.sql(expression, "this"),
    444            level=1,
    445            pad=0,
    446        )
    447        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    449    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    450        original = self.identify
    451        self.identify = False
    452        result = func(*args, **kwargs)
    453        self.identify = original
    454        return result

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

    456    def normalize_func(self, name: str) -> str:
    457        if self.normalize_functions == "upper":
    458            return name.upper()
    459        if self.normalize_functions == "lower":
    460            return name.lower()
    461        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

    463    def indent(
    464        self,
    465        sql: str,
    466        level: int = 0,
    467        pad: t.Optional[int] = None,
    468        skip_first: bool = False,
    469        skip_last: bool = False,
    470    ) -> str:
    471        if not self.pretty:
    472            return sql
    473
    474        pad = self.pad if pad is None else pad
    475        lines = sql.split("\n")
    476
    477        return "\n".join(
    478            line
    479            if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    480            else f"{' ' * (level * self._indent + pad)}{line}"
    481            for i, line in enumerate(lines)
    482        )

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

    484    def sql(
    485        self,
    486        expression: t.Optional[str | exp.Expression],
    487        key: t.Optional[str] = None,
    488        comment: bool = True,
    489    ) -> str:
    490        if not expression:
    491            return ""
    492
    493        if isinstance(expression, str):
    494            return expression
    495
    496        if key:
    497            return self.sql(expression.args.get(key))
    498
    499        if self._cache is not None:
    500            expression_id = hash(expression)
    501
    502            if expression_id in self._cache:
    503                return self._cache[expression_id]
    504
    505        transform = self.TRANSFORMS.get(expression.__class__)
    506
    507        if callable(transform):
    508            sql = transform(self, expression)
    509        elif transform:
    510            sql = transform
    511        elif isinstance(expression, exp.Expression):
    512            exp_handler_name = f"{expression.key}_sql"
    513
    514            if hasattr(self, exp_handler_name):
    515                sql = getattr(self, exp_handler_name)(expression)
    516            elif isinstance(expression, exp.Func):
    517                sql = self.function_fallback_sql(expression)
    518            elif isinstance(expression, exp.Property):
    519                sql = self.property_sql(expression)
    520            else:
    521                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    522        else:
    523            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    524
    525        sql = self.maybe_comment(sql, expression) if self._comments and comment else sql
    526
    527        if self._cache is not None:
    528            self._cache[expression_id] = sql
    529        return sql

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

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

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

    536    def cache_sql(self, expression: exp.Cache) -> str:
    537        lazy = " LAZY" if expression.args.get("lazy") else ""
    538        table = self.sql(expression, "this")
    539        options = expression.args.get("options")
    540        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    541        sql = self.sql(expression, "expression")
    542        sql = f" AS{self.sep()}{sql}" if sql else ""
    543        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    544        return self.prepend_ctes(expression, sql)

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

    546    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    547        if isinstance(expression.parent, exp.Cast):
    548            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    549        default = "DEFAULT " if expression.args.get("default") else ""
    550        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    552    def column_sql(self, expression: exp.Column) -> str:
    553        return ".".join(
    554            self.sql(part)
    555            for part in (
    556                expression.args.get("catalog"),
    557                expression.args.get("db"),
    558                expression.args.get("table"),
    559                expression.args.get("this"),
    560            )
    561            if part
    562        )

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

    564    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    565        this = self.sql(expression, "this")
    566        this = f" {this}" if this else ""
    567        position = self.sql(expression, "position")
    568        return f"{position}{this}"

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

    570    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    571        column = self.sql(expression, "this")
    572        kind = self.sql(expression, "kind")
    573        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    574        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    575        kind = f"{sep}{kind}" if kind else ""
    576        constraints = f" {constraints}" if constraints else ""
    577        position = self.sql(expression, "position")
    578        position = f" {position}" if position else ""
    579
    580        return f"{exists}{column}{kind}{constraints}{position}"

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

    582    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    583        this = self.sql(expression, "this")
    584        kind_sql = self.sql(expression, "kind").strip()
    585        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

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

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

    590    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    591        if isinstance(expression.this, list):
    592            this = self.wrap(self.expressions(expression, key="this", flat=True))
    593        else:
    594            this = self.sql(expression, "this")
    595
    596        return f"COMPRESS {this}"

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

    598    def generatedasidentitycolumnconstraint_sql(
    599        self, expression: exp.GeneratedAsIdentityColumnConstraint
    600    ) -> str:
    601        this = ""
    602        if expression.this is not None:
    603            on_null = "ON NULL " if expression.args.get("on_null") else ""
    604            this = " ALWAYS " if expression.this else f" BY DEFAULT {on_null}"
    605
    606        start = expression.args.get("start")
    607        start = f"START WITH {start}" if start else ""
    608        increment = expression.args.get("increment")
    609        increment = f" INCREMENT BY {increment}" if increment else ""
    610        minvalue = expression.args.get("minvalue")
    611        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    612        maxvalue = expression.args.get("maxvalue")
    613        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    614        cycle = expression.args.get("cycle")
    615        cycle_sql = ""
    616
    617        if cycle is not None:
    618            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    619            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    620
    621        sequence_opts = ""
    622        if start or increment or cycle_sql:
    623            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    624            sequence_opts = f" ({sequence_opts.strip()})"
    625
    626        expr = self.sql(expression, "expression")
    627        expr = f"({expr})" if expr else "IDENTITY"
    628
    629        return f"GENERATED{this}AS {expr}{sequence_opts}"

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

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

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

    634    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    635        desc = expression.args.get("desc")
    636        if desc is not None:
    637            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    638        return f"PRIMARY KEY"

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

    640    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    641        this = self.sql(expression, "this")
    642        this = f" {this}" if this else ""
    643        return f"UNIQUE{this}"

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

    645    def create_sql(self, expression: exp.Create) -> str:
    646        kind = self.sql(expression, "kind").upper()
    647        properties = expression.args.get("properties")
    648        properties_exp = expression.copy()
    649        properties_locs = self.locate_properties(properties) if properties else {}
    650        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    651            exp.Properties.Location.POST_WITH
    652        ):
    653            properties_exp.set(
    654                "properties",
    655                exp.Properties(
    656                    expressions=[
    657                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    658                        *properties_locs[exp.Properties.Location.POST_WITH],
    659                    ]
    660                ),
    661            )
    662        if kind == "TABLE" and properties_locs.get(exp.Properties.Location.POST_NAME):
    663            this_name = self.sql(expression.this, "this")
    664            this_properties = self.properties(
    665                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_NAME]),
    666                wrapped=False,
    667            )
    668            this_schema = f"({self.expressions(expression.this)})"
    669            this = f"{this_name}, {this_properties} {this_schema}"
    670            properties_sql = ""
    671        else:
    672            this = self.sql(expression, "this")
    673            properties_sql = self.sql(properties_exp, "properties")
    674        begin = " BEGIN" if expression.args.get("begin") else ""
    675        expression_sql = self.sql(expression, "expression")
    676        if expression_sql:
    677            expression_sql = f"{begin}{self.sep()}{expression_sql}"
    678
    679            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    680                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    681                    postalias_props_sql = self.properties(
    682                        exp.Properties(
    683                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    684                        ),
    685                        wrapped=False,
    686                    )
    687                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
    688                else:
    689                    expression_sql = f" AS{expression_sql}"
    690
    691        postindex_props_sql = ""
    692        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    693            postindex_props_sql = self.properties(
    694                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    695                wrapped=False,
    696                prefix=" ",
    697            )
    698
    699        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    700        indexes = f" {indexes}" if indexes else ""
    701        index_sql = indexes + postindex_props_sql
    702
    703        replace = " OR REPLACE" if expression.args.get("replace") else ""
    704        unique = " UNIQUE" if expression.args.get("unique") else ""
    705
    706        postcreate_props_sql = ""
    707        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    708            postcreate_props_sql = self.properties(
    709                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    710                sep=" ",
    711                prefix=" ",
    712                wrapped=False,
    713            )
    714
    715        modifiers = "".join((replace, unique, postcreate_props_sql))
    716
    717        postexpression_props_sql = ""
    718        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    719            postexpression_props_sql = self.properties(
    720                exp.Properties(
    721                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    722                ),
    723                sep=" ",
    724                prefix=" ",
    725                wrapped=False,
    726            )
    727
    728        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    729        no_schema_binding = (
    730            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    731        )
    732
    733        clone = self.sql(expression, "clone")
    734        clone = f" {clone}" if clone else ""
    735
    736        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    737        return self.prepend_ctes(expression, expression_sql)

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

    739    def clone_sql(self, expression: exp.Clone) -> str:
    740        this = self.sql(expression, "this")
    741        when = self.sql(expression, "when")
    742
    743        if when:
    744            kind = self.sql(expression, "kind")
    745            expr = self.sql(expression, "expression")
    746            return f"CLONE {this} {when} ({kind} => {expr})"
    747
    748        return f"CLONE {this}"

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

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

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

    753    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    754        with_ = self.sql(expression, "with")
    755        if with_:
    756            sql = f"{with_}{self.sep()}{sql}"
    757        return sql

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

    759    def with_sql(self, expression: exp.With) -> str:
    760        sql = self.expressions(expression, flat=True)
    761        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
    762
    763        return f"WITH {recursive}{sql}"

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

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

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

    769    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    770        alias = self.sql(expression, "this")
    771        columns = self.expressions(expression, key="columns", flat=True)
    772        columns = f"({columns})" if columns else ""
    773        return f"{alias}{columns}"

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

    775    def bitstring_sql(self, expression: exp.BitString) -> str:
    776        this = self.sql(expression, "this")
    777        if self.bit_start:
    778            return f"{self.bit_start}{this}{self.bit_end}"
    779        return f"{int(this, 2)}"

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

    781    def hexstring_sql(self, expression: exp.HexString) -> str:
    782        this = self.sql(expression, "this")
    783        if self.hex_start:
    784            return f"{self.hex_start}{this}{self.hex_end}"
    785        return f"{int(this, 16)}"

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

    787    def bytestring_sql(self, expression: exp.ByteString) -> str:
    788        this = self.sql(expression, "this")
    789        if self.byte_start:
    790            return f"{self.byte_start}{this}{self.byte_end}"
    791        return this

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

    793    def rawstring_sql(self, expression: exp.RawString) -> str:
    794        if self.raw_start:
    795            return f"{self.raw_start}{expression.name}{self.raw_end}"
    796        return self.sql(exp.Literal.string(expression.name.replace("\\", "\\\\")))

def datatypesize_sql(self, expression: sqlglot.expressions.DataTypeSize) -> str: View Source

    798    def datatypesize_sql(self, expression: exp.DataTypeSize) -> str:
    799        this = self.sql(expression, "this")
    800        specifier = self.sql(expression, "expression")
    801        specifier = f" {specifier}" if specifier else ""
    802        return f"{this}{specifier}"

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

    804    def datatype_sql(self, expression: exp.DataType) -> str:
    805        type_value = expression.this
    806        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
    807        nested = ""
    808        interior = self.expressions(expression, flat=True)
    809        values = ""
    810        if interior:
    811            if expression.args.get("nested"):
    812                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    813                if expression.args.get("values") is not None:
    814                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    815                    values = self.expressions(expression, key="values", flat=True)
    816                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    817            else:
    818                nested = f"({interior})"
    819
    820        return f"{type_sql}{nested}{values}"

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

    822    def directory_sql(self, expression: exp.Directory) -> str:
    823        local = "LOCAL " if expression.args.get("local") else ""
    824        row_format = self.sql(expression, "row_format")
    825        row_format = f" {row_format}" if row_format else ""
    826        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    828    def delete_sql(self, expression: exp.Delete) -> str:
    829        this = self.sql(expression, "this")
    830        this = f" FROM {this}" if this else ""
    831        using_sql = (
    832            f" USING {self.expressions(expression, key='using', sep=', USING ')}"
    833            if expression.args.get("using")
    834            else ""
    835        )
    836        where_sql = self.sql(expression, "where")
    837        returning = self.sql(expression, "returning")
    838        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
    839        return self.prepend_ctes(expression, sql)

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

    841    def drop_sql(self, expression: exp.Drop) -> str:
    842        this = self.sql(expression, "this")
    843        kind = expression.args["kind"]
    844        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    845        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    846        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    847        cascade = " CASCADE" if expression.args.get("cascade") else ""
    848        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    849        purge = " PURGE" if expression.args.get("purge") else ""
    850        return (
    851            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
    852        )

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

    854    def except_sql(self, expression: exp.Except) -> str:
    855        return self.prepend_ctes(
    856            expression,
    857            self.set_operation(expression, self.except_op(expression)),
    858        )

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

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

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

    863    def fetch_sql(self, expression: exp.Fetch) -> str:
    864        direction = expression.args.get("direction")
    865        direction = f" {direction.upper()}" if direction else ""
    866        count = expression.args.get("count")
    867        count = f" {count}" if count else ""
    868        if expression.args.get("percent"):
    869            count = f"{count} PERCENT"
    870        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    871        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

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

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

    878    def hint_sql(self, expression: exp.Hint) -> str:
    879        if self.sql(expression, "this"):
    880            self.unsupported("Hints are not supported")
    881        return ""

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

    883    def index_sql(self, expression: exp.Index) -> str:
    884        unique = "UNIQUE " if expression.args.get("unique") else ""
    885        primary = "PRIMARY " if expression.args.get("primary") else ""
    886        amp = "AMP " if expression.args.get("amp") else ""
    887        name = f"{expression.name} " if expression.name else ""
    888        table = self.sql(expression, "table")
    889        table = f"{self.INDEX_ON} {table} " if table else ""
    890        index = "INDEX " if not table else ""
    891        columns = self.expressions(expression, key="columns", flat=True)
    892        partition_by = self.expressions(expression, key="partition_by", flat=True)
    893        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    894        return f"{unique}{primary}{amp}{index}{name}{table}({columns}){partition_by}"

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

    896    def identifier_sql(self, expression: exp.Identifier) -> str:
    897        text = expression.name
    898        lower = text.lower()
    899        text = lower if self.normalize and not expression.quoted else text
    900        text = text.replace(self.identifier_end, self._escaped_identifier_end)
    901        if (
    902            expression.quoted
    903            or should_identify(text, self.identify)
    904            or lower in self.RESERVED_KEYWORDS
    905            or (not self.identifiers_can_start_with_digit and text[:1].isdigit())
    906        ):
    907            text = f"{self.identifier_start}{text}{self.identifier_end}"
    908        return text

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

    910    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    911        input_format = self.sql(expression, "input_format")
    912        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    913        output_format = self.sql(expression, "output_format")
    914        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    915        return self.sep().join((input_format, output_format))

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

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

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

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

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

    924    def properties_sql(self, expression: exp.Properties) -> str:
    925        root_properties = []
    926        with_properties = []
    927
    928        for p in expression.expressions:
    929            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    930            if p_loc == exp.Properties.Location.POST_WITH:
    931                with_properties.append(p)
    932            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    933                root_properties.append(p)
    934
    935        return self.root_properties(
    936            exp.Properties(expressions=root_properties)
    937        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    939    def root_properties(self, properties: exp.Properties) -> str:
    940        if properties.expressions:
    941            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    942        return ""

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

    944    def properties(
    945        self,
    946        properties: exp.Properties,
    947        prefix: str = "",
    948        sep: str = ", ",
    949        suffix: str = "",
    950        wrapped: bool = True,
    951    ) -> str:
    952        if properties.expressions:
    953            expressions = self.expressions(properties, sep=sep, indent=False)
    954            expressions = self.wrap(expressions) if wrapped else expressions
    955            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
    956        return ""

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

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

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

    961    def locate_properties(
    962        self, properties: exp.Properties
    963    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
    964        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
    965            key: [] for key in exp.Properties.Location
    966        }
    967
    968        for p in properties.expressions:
    969            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    970            if p_loc == exp.Properties.Location.POST_NAME:
    971                properties_locs[exp.Properties.Location.POST_NAME].append(p)
    972            elif p_loc == exp.Properties.Location.POST_INDEX:
    973                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
    974            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    975                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
    976            elif p_loc == exp.Properties.Location.POST_WITH:
    977                properties_locs[exp.Properties.Location.POST_WITH].append(p)
    978            elif p_loc == exp.Properties.Location.POST_CREATE:
    979                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
    980            elif p_loc == exp.Properties.Location.POST_ALIAS:
    981                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
    982            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
    983                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
    984            elif p_loc == exp.Properties.Location.UNSUPPORTED:
    985                self.unsupported(f"Unsupported property {p.key}")
    986
    987        return properties_locs

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

    989    def property_sql(self, expression: exp.Property) -> str:
    990        property_cls = expression.__class__
    991        if property_cls == exp.Property:
    992            return f"{expression.name}={self.sql(expression, 'value')}"
    993
    994        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    995        if not property_name:
    996            self.unsupported(f"Unsupported property {expression.key}")
    997
    998        return f"{property_name}={self.sql(expression, 'this')}"

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

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

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

    1005    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1006        no = "NO " if expression.args.get("no") else ""
    1007        protection = " PROTECTION" if expression.args.get("protection") else ""
    1008        return f"{no}FALLBACK{protection}"

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

    1010    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1011        no = "NO " if expression.args.get("no") else ""
    1012        local = expression.args.get("local")
    1013        local = f"{local} " if local else ""
    1014        dual = "DUAL " if expression.args.get("dual") else ""
    1015        before = "BEFORE " if expression.args.get("before") else ""
    1016        after = "AFTER " if expression.args.get("after") else ""
    1017        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1019    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1020        freespace = self.sql(expression, "this")
    1021        percent = " PERCENT" if expression.args.get("percent") else ""
    1022        return f"FREESPACE={freespace}{percent}"

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

    1024    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1025        if expression.args.get("default"):
    1026            property = "DEFAULT"
    1027        elif expression.args.get("on"):
    1028            property = "ON"
    1029        else:
    1030            property = "OFF"
    1031        return f"CHECKSUM={property}"

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

    1033    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1034        if expression.args.get("no"):
    1035            return "NO MERGEBLOCKRATIO"
    1036        if expression.args.get("default"):
    1037            return "DEFAULT MERGEBLOCKRATIO"
    1038
    1039        percent = " PERCENT" if expression.args.get("percent") else ""
    1040        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1042    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1043        default = expression.args.get("default")
    1044        minimum = expression.args.get("minimum")
    1045        maximum = expression.args.get("maximum")
    1046        if default or minimum or maximum:
    1047            if default:
    1048                prop = "DEFAULT"
    1049            elif minimum:
    1050                prop = "MINIMUM"
    1051            else:
    1052                prop = "MAXIMUM"
    1053            return f"{prop} DATABLOCKSIZE"
    1054        units = expression.args.get("units")
    1055        units = f" {units}" if units else ""
    1056        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1058    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1059        autotemp = expression.args.get("autotemp")
    1060        always = expression.args.get("always")
    1061        default = expression.args.get("default")
    1062        manual = expression.args.get("manual")
    1063        never = expression.args.get("never")
    1064
    1065        if autotemp is not None:
    1066            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1067        elif always:
    1068            prop = "ALWAYS"
    1069        elif default:
    1070            prop = "DEFAULT"
    1071        elif manual:
    1072            prop = "MANUAL"
    1073        elif never:
    1074            prop = "NEVER"
    1075        return f"BLOCKCOMPRESSION={prop}"

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

    1077    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1078        no = expression.args.get("no")
    1079        no = " NO" if no else ""
    1080        concurrent = expression.args.get("concurrent")
    1081        concurrent = " CONCURRENT" if concurrent else ""
    1082
    1083        for_ = ""
    1084        if expression.args.get("for_all"):
    1085            for_ = " FOR ALL"
    1086        elif expression.args.get("for_insert"):
    1087            for_ = " FOR INSERT"
    1088        elif expression.args.get("for_none"):
    1089            for_ = " FOR NONE"
    1090        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

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

    1092    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1093        kind = expression.args.get("kind")
    1094        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1095        for_or_in = expression.args.get("for_or_in")
    1096        lock_type = expression.args.get("lock_type")
    1097        override = " OVERRIDE" if expression.args.get("override") else ""
    1098        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"

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

    1100    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1101        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1102        statistics = expression.args.get("statistics")
    1103        statistics_sql = ""
    1104        if statistics is not None:
    1105            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1106        return f"{data_sql}{statistics_sql}"

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

    1108    def insert_sql(self, expression: exp.Insert) -> str:
    1109        overwrite = expression.args.get("overwrite")
    1110
    1111        if isinstance(expression.this, exp.Directory):
    1112            this = "OVERWRITE " if overwrite else "INTO "
    1113        else:
    1114            this = "OVERWRITE TABLE " if overwrite else "INTO "
    1115
    1116        alternative = expression.args.get("alternative")
    1117        alternative = f" OR {alternative} " if alternative else " "
    1118        this = f"{this}{self.sql(expression, 'this')}"
    1119
    1120        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1121        partition_sql = (
    1122            self.sql(expression, "partition") if expression.args.get("partition") else ""
    1123        )
    1124        expression_sql = self.sql(expression, "expression")
    1125        conflict = self.sql(expression, "conflict")
    1126        returning = self.sql(expression, "returning")
    1127        sep = self.sep() if partition_sql else ""
    1128        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{conflict}{returning}"
    1129        return self.prepend_ctes(expression, sql)

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

    1131    def intersect_sql(self, expression: exp.Intersect) -> str:
    1132        return self.prepend_ctes(
    1133            expression,
    1134            self.set_operation(expression, self.intersect_op(expression)),
    1135        )

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

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

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

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

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

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

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

    1146    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1147        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1148        constraint = self.sql(expression, "constraint")
    1149        if constraint:
    1150            constraint = f"ON CONSTRAINT {constraint}"
    1151        key = self.expressions(expression, key="key", flat=True)
    1152        do = "" if expression.args.get("duplicate") else " DO "
    1153        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1154        expressions = self.expressions(expression, flat=True)
    1155        if expressions:
    1156            expressions = f"UPDATE SET {expressions}"
    1157        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"

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

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

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

    1162    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1163        fields = expression.args.get("fields")
    1164        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1165        escaped = expression.args.get("escaped")
    1166        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1167        items = expression.args.get("collection_items")
    1168        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1169        keys = expression.args.get("map_keys")
    1170        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1171        lines = expression.args.get("lines")
    1172        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1173        null = expression.args.get("null")
    1174        null = f" NULL DEFINED AS {null}" if null else ""
    1175        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

    1177    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1178        table = ".".join(
    1179            part
    1180            for part in [
    1181                self.sql(expression, "catalog"),
    1182                self.sql(expression, "db"),
    1183                self.sql(expression, "this"),
    1184            ]
    1185            if part
    1186        )
    1187
    1188        alias = self.sql(expression, "alias")
    1189        alias = f"{sep}{alias}" if alias else ""
    1190        hints = self.expressions(expression, key="hints", flat=True)
    1191        hints = f" WITH ({hints})" if hints and self.TABLE_HINTS else ""
    1192        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1193        pivots = f" {pivots}" if pivots else ""
    1194        joins = self.expressions(expression, key="joins", sep="")
    1195        laterals = self.expressions(expression, key="laterals", sep="")
    1196        system_time = expression.args.get("system_time")
    1197        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
    1198
    1199        return f"{table}{system_time}{alias}{hints}{pivots}{joins}{laterals}"

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

    1201    def tablesample_sql(
    1202        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1203    ) -> str:
    1204        if self.alias_post_tablesample and expression.this.alias:
    1205            table = expression.this.copy()
    1206            table.set("alias", None)
    1207            this = self.sql(table)
    1208            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1209        else:
    1210            this = self.sql(expression, "this")
    1211            alias = ""
    1212        method = self.sql(expression, "method")
    1213        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1214        numerator = self.sql(expression, "bucket_numerator")
    1215        denominator = self.sql(expression, "bucket_denominator")
    1216        field = self.sql(expression, "bucket_field")
    1217        field = f" ON {field}" if field else ""
    1218        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1219        percent = self.sql(expression, "percent")
    1220        percent = f"{percent} PERCENT" if percent else ""
    1221        rows = self.sql(expression, "rows")
    1222        rows = f"{rows} ROWS" if rows else ""
    1223        size = self.sql(expression, "size")
    1224        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1225            size = f"{size} PERCENT"
    1226        seed = self.sql(expression, "seed")
    1227        seed = f" {seed_prefix} ({seed})" if seed else ""
    1228        kind = expression.args.get("kind", "TABLESAMPLE")
    1229        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"

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

    1231    def pivot_sql(self, expression: exp.Pivot) -> str:
    1232        expressions = self.expressions(expression, flat=True)
    1233
    1234        if expression.this:
    1235            this = self.sql(expression, "this")
    1236            on = f"{self.seg('ON')} {expressions}"
    1237            using = self.expressions(expression, key="using", flat=True)
    1238            using = f"{self.seg('USING')} {using}" if using else ""
    1239            group = self.sql(expression, "group")
    1240            return f"PIVOT {this}{on}{using}{group}"
    1241
    1242        alias = self.sql(expression, "alias")
    1243        alias = f" AS {alias}" if alias else ""
    1244        unpivot = expression.args.get("unpivot")
    1245        direction = "UNPIVOT" if unpivot else "PIVOT"
    1246        field = self.sql(expression, "field")
    1247        return f"{direction}({expressions} FOR {field}){alias}"

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

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

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

    1252    def update_sql(self, expression: exp.Update) -> str:
    1253        this = self.sql(expression, "this")
    1254        set_sql = self.expressions(expression, flat=True)
    1255        from_sql = self.sql(expression, "from")
    1256        where_sql = self.sql(expression, "where")
    1257        returning = self.sql(expression, "returning")
    1258        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
    1259        return self.prepend_ctes(expression, sql)

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

    1261    def values_sql(self, expression: exp.Values) -> str:
    1262        args = self.expressions(expression)
    1263        alias = self.sql(expression, "alias")
    1264        values = f"VALUES{self.seg('')}{args}"
    1265        values = (
    1266            f"({values})"
    1267            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1268            else values
    1269        )
    1270        return f"{values} AS {alias}" if alias else values

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

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

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

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

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

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

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

    1283    def group_sql(self, expression: exp.Group) -> str:
    1284        group_by = self.op_expressions("GROUP BY", expression)
    1285        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1286        grouping_sets = (
    1287            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1288        )
    1289
    1290        cube = expression.args.get("cube", [])
    1291        if seq_get(cube, 0) is True:
    1292            return f"{group_by}{self.seg('WITH CUBE')}"
    1293        else:
    1294            cube_sql = self.expressions(expression, key="cube", indent=False)
    1295            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1296
    1297        rollup = expression.args.get("rollup", [])
    1298        if seq_get(rollup, 0) is True:
    1299            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1300        else:
    1301            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1302            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1303
    1304        groupings = csv(
    1305            grouping_sets,
    1306            cube_sql,
    1307            rollup_sql,
    1308            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1309            sep=self.GROUPINGS_SEP,
    1310        )
    1311
    1312        if expression.args.get("expressions") and groupings:
    1313            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1314
    1315        return f"{group_by}{groupings}"

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

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

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

    1321    def join_sql(self, expression: exp.Join) -> str:
    1322        op_sql = " ".join(
    1323            op
    1324            for op in (
    1325                expression.method,
    1326                "GLOBAL" if expression.args.get("global") else None,
    1327                expression.side,
    1328                expression.kind,
    1329                expression.hint if self.JOIN_HINTS else None,
    1330            )
    1331            if op
    1332        )
    1333        on_sql = self.sql(expression, "on")
    1334        using = expression.args.get("using")
    1335
    1336        if not on_sql and using:
    1337            on_sql = csv(*(self.sql(column) for column in using))
    1338
    1339        this_sql = self.sql(expression, "this")
    1340
    1341        if on_sql:
    1342            on_sql = self.indent(on_sql, skip_first=True)
    1343            space = self.seg(" " * self.pad) if self.pretty else " "
    1344            if using:
    1345                on_sql = f"{space}USING ({on_sql})"
    1346            else:
    1347                on_sql = f"{space}ON {on_sql}"
    1348        elif not op_sql:
    1349            return f", {this_sql}"
    1350
    1351        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1352        return f"{self.seg(op_sql)} {this_sql}{on_sql}"

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

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

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

    1359    def lateral_sql(self, expression: exp.Lateral) -> str:
    1360        this = self.sql(expression, "this")
    1361
    1362        if isinstance(expression.this, exp.Subquery):
    1363            return f"LATERAL {this}"
    1364
    1365        if expression.args.get("view"):
    1366            alias = expression.args["alias"]
    1367            columns = self.expressions(alias, key="columns", flat=True)
    1368            table = f" {alias.name}" if alias.name else ""
    1369            columns = f" AS {columns}" if columns else ""
    1370            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1371            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1372
    1373        alias = self.sql(expression, "alias")
    1374        alias = f" AS {alias}" if alias else ""
    1375        return f"LATERAL {this}{alias}"

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

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

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

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

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

    1385    def setitem_sql(self, expression: exp.SetItem) -> str:
    1386        kind = self.sql(expression, "kind")
    1387        kind = f"{kind} " if kind else ""
    1388        this = self.sql(expression, "this")
    1389        expressions = self.expressions(expression)
    1390        collate = self.sql(expression, "collate")
    1391        collate = f" COLLATE {collate}" if collate else ""
    1392        global_ = "GLOBAL " if expression.args.get("global") else ""
    1393        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1395    def set_sql(self, expression: exp.Set) -> str:
    1396        expressions = (
    1397            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1398        )
    1399        return f"SET{expressions}"

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

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

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

    1404    def lock_sql(self, expression: exp.Lock) -> str:
    1405        if not self.LOCKING_READS_SUPPORTED:
    1406            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1407            return ""
    1408
    1409        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1410        expressions = self.expressions(expression, flat=True)
    1411        expressions = f" OF {expressions}" if expressions else ""
    1412        wait = expression.args.get("wait")
    1413
    1414        if wait is not None:
    1415            if isinstance(wait, exp.Literal):
    1416                wait = f" WAIT {self.sql(wait)}"
    1417            else:
    1418                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1419
    1420        return f"{lock_type}{expressions}{wait or ''}"

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

    1422    def literal_sql(self, expression: exp.Literal) -> str:
    1423        text = expression.this or ""
    1424        if expression.is_string:
    1425            text = text.replace(self.quote_end, self._escaped_quote_end)
    1426            if self.pretty:
    1427                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1428            text = f"{self.quote_start}{text}{self.quote_end}"
    1429        return text

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

    1431    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1432        local = " LOCAL" if expression.args.get("local") else ""
    1433        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1434        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1435        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1436        partition = self.sql(expression, "partition")
    1437        partition = f" {partition}" if partition else ""
    1438        input_format = self.sql(expression, "input_format")
    1439        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1440        serde = self.sql(expression, "serde")
    1441        serde = f" SERDE {serde}" if serde else ""
    1442        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1444    def null_sql(self, *_) -> str:
    1445        return "NULL"

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

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

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

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

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

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

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

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

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

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

    1464    def ordered_sql(self, expression: exp.Ordered) -> str:
    1465        desc = expression.args.get("desc")
    1466        asc = not desc
    1467
    1468        nulls_first = expression.args.get("nulls_first")
    1469        nulls_last = not nulls_first
    1470        nulls_are_large = self.null_ordering == "nulls_are_large"
    1471        nulls_are_small = self.null_ordering == "nulls_are_small"
    1472        nulls_are_last = self.null_ordering == "nulls_are_last"
    1473
    1474        sort_order = " DESC" if desc else ""
    1475        nulls_sort_change = ""
    1476        if nulls_first and (
    1477            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1478        ):
    1479            nulls_sort_change = " NULLS FIRST"
    1480        elif (
    1481            nulls_last
    1482            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1483            and not nulls_are_last
    1484        ):
    1485            nulls_sort_change = " NULLS LAST"
    1486
    1487        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1488            self.unsupported(
    1489                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1490            )
    1491            nulls_sort_change = ""
    1492
    1493        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

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

    1495    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1496        partition = self.partition_by_sql(expression)
    1497        order = self.sql(expression, "order")
    1498        measures = self.expressions(expression, key="measures")
    1499        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1500        rows = self.sql(expression, "rows")
    1501        rows = self.seg(rows) if rows else ""
    1502        after = self.sql(expression, "after")
    1503        after = self.seg(after) if after else ""
    1504        pattern = self.sql(expression, "pattern")
    1505        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1506        definition_sqls = [
    1507            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1508            for definition in expression.args.get("define", [])
    1509        ]
    1510        definitions = self.expressions(sqls=definition_sqls)
    1511        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1512        body = "".join(
    1513            (
    1514                partition,
    1515                order,
    1516                measures,
    1517                rows,
    1518                after,
    1519                pattern,
    1520                define,
    1521            )
    1522        )
    1523        alias = self.sql(expression, "alias")
    1524        alias = f" {alias}" if alias else ""
    1525        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    1527    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1528        limit = expression.args.get("limit")
    1529
    1530        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1531            limit = exp.Limit(expression=limit.args.get("count"))
    1532        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1533            limit = exp.Fetch(direction="FIRST", count=limit.expression)
    1534
    1535        fetch = isinstance(limit, exp.Fetch)
    1536
    1537        return csv(
    1538            *sqls,
    1539            *[self.sql(join) for join in expression.args.get("joins") or []],
    1540            self.sql(expression, "match"),
    1541            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1542            self.sql(expression, "where"),
    1543            self.sql(expression, "group"),
    1544            self.sql(expression, "having"),
    1545            *self.after_having_modifiers(expression),
    1546            self.sql(expression, "order"),
    1547            self.sql(expression, "offset") if fetch else self.sql(limit),
    1548            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1549            *self.after_limit_modifiers(expression),
    1550            sep="",
    1551        )

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

    1553    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1554        return [
    1555            self.sql(expression, "qualify"),
    1556            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1557            if expression.args.get("windows")
    1558            else "",
    1559        ]

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

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

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

    1566    def select_sql(self, expression: exp.Select) -> str:
    1567        hint = self.sql(expression, "hint")
    1568        distinct = self.sql(expression, "distinct")
    1569        distinct = f" {distinct}" if distinct else ""
    1570        kind = expression.args.get("kind")
    1571        kind = f" AS {kind}" if kind else ""
    1572        expressions = self.expressions(expression)
    1573        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1574        sql = self.query_modifiers(
    1575            expression,
    1576            f"SELECT{hint}{distinct}{kind}{expressions}",
    1577            self.sql(expression, "into", comment=False),
    1578            self.sql(expression, "from", comment=False),
    1579        )
    1580        return self.prepend_ctes(expression, sql)

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

    1582    def schema_sql(self, expression: exp.Schema) -> str:
    1583        this = self.sql(expression, "this")
    1584        this = f"{this} " if this else ""
    1585        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1586        return f"{this}{sql}"

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

    1588    def star_sql(self, expression: exp.Star) -> str:
    1589        except_ = self.expressions(expression, key="except", flat=True)
    1590        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1591        replace = self.expressions(expression, key="replace", flat=True)
    1592        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1593        return f"*{except_}{replace}"

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

    1595    def parameter_sql(self, expression: exp.Parameter) -> str:
    1596        this = self.sql(expression, "this")
    1597        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1598        return f"{self.PARAMETER_TOKEN}{this}"

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

    1600    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1601        this = self.sql(expression, "this")
    1602        kind = expression.text("kind")
    1603        if kind:
    1604            kind = f"{kind}."
    1605        return f"@@{kind}{this}"

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

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

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

    1610    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1611        alias = self.sql(expression, "alias")
    1612        alias = f"{sep}{alias}" if alias else ""
    1613
    1614        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1615        pivots = f" {pivots}" if pivots else ""
    1616
    1617        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    1618        return self.prepend_ctes(expression, sql)

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

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

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

    1624    def union_sql(self, expression: exp.Union) -> str:
    1625        return self.prepend_ctes(
    1626            expression,
    1627            self.set_operation(expression, self.union_op(expression)),
    1628        )

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

    1630    def union_op(self, expression: exp.Union) -> str:
    1631        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1632        kind = kind if expression.args.get("distinct") else " ALL"
    1633        return f"UNION{kind}"

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

    1635    def unnest_sql(self, expression: exp.Unnest) -> str:
    1636        args = self.expressions(expression, flat=True)
    1637        alias = expression.args.get("alias")
    1638        if alias and self.unnest_column_only:
    1639            columns = alias.columns
    1640            alias = self.sql(columns[0]) if columns else ""
    1641        else:
    1642            alias = self.sql(expression, "alias")
    1643        alias = f" AS {alias}" if alias else alias
    1644        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1645        offset = expression.args.get("offset")
    1646        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1647        return f"UNNEST({args}){ordinality}{alias}{offset}"

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

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

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

    1653    def window_sql(self, expression: exp.Window) -> str:
    1654        this = self.sql(expression, "this")
    1655        partition = self.partition_by_sql(expression)
    1656        order = expression.args.get("order")
    1657        order = self.order_sql(order, flat=True) if order else ""
    1658        spec = self.sql(expression, "spec")
    1659        alias = self.sql(expression, "alias")
    1660        over = self.sql(expression, "over") or "OVER"
    1661
    1662        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    1663
    1664        first = expression.args.get("first")
    1665        if first is None:
    1666            first = ""
    1667        else:
    1668            first = "FIRST" if first else "LAST"
    1669
    1670        if not partition and not order and not spec and alias:
    1671            return f"{this} {alias}"
    1672
    1673        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    1674        return f"{this} ({args})"

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

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

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

    1680    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    1681        kind = self.sql(expression, "kind")
    1682        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1683        end = (
    1684            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1685            or "CURRENT ROW"
    1686        )
    1687        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    1694    def between_sql(self, expression: exp.Between) -> str:
    1695        this = self.sql(expression, "this")
    1696        low = self.sql(expression, "low")
    1697        high = self.sql(expression, "high")
    1698        return f"{this} BETWEEN {low} AND {high}"

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

    1700    def bracket_sql(self, expression: exp.Bracket) -> str:
    1701        expressions = apply_index_offset(expression.this, expression.expressions, self.index_offset)
    1702        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1703
    1704        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    1709    def any_sql(self, expression: exp.Any) -> str:
    1710        this = self.sql(expression, "this")
    1711        if isinstance(expression.this, exp.Subqueryable):
    1712            this = self.wrap(this)
    1713        return f"ANY {this}"

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

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

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

    1718    def case_sql(self, expression: exp.Case) -> str:
    1719        this = self.sql(expression, "this")
    1720        statements = [f"CASE {this}" if this else "CASE"]
    1721
    1722        for e in expression.args["ifs"]:
    1723            statements.append(f"WHEN {self.sql(e, 'this')}")
    1724            statements.append(f"THEN {self.sql(e, 'true')}")
    1725
    1726        default = self.sql(expression, "default")
    1727
    1728        if default:
    1729            statements.append(f"ELSE {default}")
    1730
    1731        statements.append("END")
    1732
    1733        if self.pretty and self.text_width(statements) > self._max_text_width:
    1734            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1735
    1736        return " ".join(statements)

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

    1738    def constraint_sql(self, expression: exp.Constraint) -> str:
    1739        this = self.sql(expression, "this")
    1740        expressions = self.expressions(expression, flat=True)
    1741        return f"CONSTRAINT {this} {expressions}"

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

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

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

    1748    def extract_sql(self, expression: exp.Extract) -> str:
    1749        this = self.sql(expression, "this")
    1750        expression_sql = self.sql(expression, "expression")
    1751        return f"EXTRACT({this} FROM {expression_sql})"

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

    1753    def trim_sql(self, expression: exp.Trim) -> str:
    1754        trim_type = self.sql(expression, "position")
    1755
    1756        if trim_type == "LEADING":
    1757            return self.func("LTRIM", expression.this)
    1758        elif trim_type == "TRAILING":
    1759            return self.func("RTRIM", expression.this)
    1760        else:
    1761            return self.func("TRIM", expression.this, expression.expression)

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

    1763    def concat_sql(self, expression: exp.Concat) -> str:
    1764        if len(expression.expressions) == 1:
    1765            return self.sql(expression.expressions[0])
    1766        return self.function_fallback_sql(expression)

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

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

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

    1772    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1773        expressions = self.expressions(expression, flat=True)
    1774        reference = self.sql(expression, "reference")
    1775        reference = f" {reference}" if reference else ""
    1776        delete = self.sql(expression, "delete")
    1777        delete = f" ON DELETE {delete}" if delete else ""
    1778        update = self.sql(expression, "update")
    1779        update = f" ON UPDATE {update}" if update else ""
    1780        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    1782    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1783        expressions = self.expressions(expression, flat=True)
    1784        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1785        options = f" {options}" if options else ""
    1786        return f"PRIMARY KEY ({expressions}){options}"

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

    1788    def if_sql(self, expression: exp.If) -> str:
    1789        return self.case_sql(
    1790            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1791        )

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

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

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

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

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

    1801    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    1802        expressions = self.expressions(expression)
    1803        null_handling = expression.args.get("null_handling")
    1804        null_handling = f" {null_handling}" if null_handling else ""
    1805        unique_keys = expression.args.get("unique_keys")
    1806        if unique_keys is not None:
    1807            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    1808        else:
    1809            unique_keys = ""
    1810        return_type = self.sql(expression, "return_type")
    1811        return_type = f" RETURNING {return_type}" if return_type else ""
    1812        format_json = " FORMAT JSON" if expression.args.get("format_json") else ""
    1813        encoding = self.sql(expression, "encoding")
    1814        encoding = f" ENCODING {encoding}" if encoding else ""
    1815        return f"JSON_OBJECT({expressions}{null_handling}{unique_keys}{return_type}{format_json}{encoding})"

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

    1817    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    1818        this = self.sql(expression, "this")
    1819        kind = self.sql(expression, "kind")
    1820        path = self.sql(expression, "path")
    1821        path = f" {path}" if path else ""
    1822        as_json = " AS JSON" if expression.args.get("as_json") else ""
    1823        return f"{this} {kind}{path}{as_json}"

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

    1825    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    1826        this = self.sql(expression, "this")
    1827        path = self.sql(expression, "path")
    1828        path = f", {path}" if path else ""
    1829        expressions = self.expressions(expression)
    1830        with_ = (
    1831            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    1832            if expressions
    1833            else ""
    1834        )
    1835        return f"OPENJSON({this}{path}){with_}"

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

    1837    def in_sql(self, expression: exp.In) -> str:
    1838        query = expression.args.get("query")
    1839        unnest = expression.args.get("unnest")
    1840        field = expression.args.get("field")
    1841        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1842
    1843        if query:
    1844            in_sql = self.wrap(query)
    1845        elif unnest:
    1846            in_sql = self.in_unnest_op(unnest)
    1847        elif field:
    1848            in_sql = self.sql(field)
    1849        else:
    1850            in_sql = f"({self.expressions(expression, flat=True)})"
    1851
    1852        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    1857    def interval_sql(self, expression: exp.Interval) -> str:
    1858        unit = self.sql(expression, "unit")
    1859        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    1860            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    1861        unit = f" {unit}" if unit else ""
    1862
    1863        if self.SINGLE_STRING_INTERVAL:
    1864            this = expression.this.name if expression.this else ""
    1865            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    1866
    1867        this = self.sql(expression, "this")
    1868        if this:
    1869            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    1870            this = f" {this}" if unwrapped else f" ({this})"
    1871
    1872        return f"INTERVAL{this}{unit}"

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

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

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

    1877    def reference_sql(self, expression: exp.Reference) -> str:
    1878        this = self.sql(expression, "this")
    1879        expressions = self.expressions(expression, flat=True)
    1880        expressions = f"({expressions})" if expressions else ""
    1881        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1882        options = f" {options}" if options else ""
    1883        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    1888    def paren_sql(self, expression: exp.Paren) -> str:
    1889        if isinstance(expression.unnest(), exp.Select):
    1890            sql = self.wrap(expression)
    1891        else:
    1892            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1893            sql = f"({sql}{self.seg(')', sep='')}"
    1894
    1895        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    1906    def alias_sql(self, expression: exp.Alias) -> str:
    1907        alias = self.sql(expression, "alias")
    1908        alias = f" AS {alias}" if alias else ""
    1909        return f"{self.sql(expression, 'this')}{alias}"

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

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

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

    1914    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1915        this = self.sql(expression, "this")
    1916        zone = self.sql(expression, "zone")
    1917        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

    1925    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1926        if not self.pretty:
    1927            return self.binary(expression, op)
    1928
    1929        sqls = tuple(
    1930            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    1931            for i, e in enumerate(expression.flatten(unnest=False))
    1932        )
    1933
    1934        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1935        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1968    def comment_sql(self, expression: exp.Comment) -> str:
    1969        this = self.sql(expression, "this")
    1970        kind = expression.args["kind"]
    1971        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1972        expression_sql = self.sql(expression, "expression")
    1973        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    1975    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    1976        this = self.sql(expression, "this")
    1977        delete = " DELETE" if expression.args.get("delete") else ""
    1978        recompress = self.sql(expression, "recompress")
    1979        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    1980        to_disk = self.sql(expression, "to_disk")
    1981        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    1982        to_volume = self.sql(expression, "to_volume")
    1983        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    1984        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    1986    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    1987        where = self.sql(expression, "where")
    1988        group = self.sql(expression, "group")
    1989        aggregates = self.expressions(expression, key="aggregates")
    1990        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    1991
    1992        if not (where or group or aggregates) and len(expression.expressions) == 1:
    1993            return f"TTL {self.expressions(expression, flat=True)}"
    1994
    1995        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2000    def commit_sql(self, expression: exp.Commit) -> str:
    2001        chain = expression.args.get("chain")
    2002        if chain is not None:
    2003            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2004
    2005        return f"COMMIT{chain or ''}"

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

    2007    def rollback_sql(self, expression: exp.Rollback) -> str:
    2008        savepoint = expression.args.get("savepoint")
    2009        savepoint = f" TO {savepoint}" if savepoint else ""
    2010        return f"ROLLBACK{savepoint}"

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

    2012    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2013        this = self.sql(expression, "this")
    2014
    2015        dtype = self.sql(expression, "dtype")
    2016        if dtype:
    2017            collate = self.sql(expression, "collate")
    2018            collate = f" COLLATE {collate}" if collate else ""
    2019            using = self.sql(expression, "using")
    2020            using = f" USING {using}" if using else ""
    2021            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    2022
    2023        default = self.sql(expression, "default")
    2024        if default:
    2025            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2026
    2027        if not expression.args.get("drop"):
    2028            self.unsupported("Unsupported ALTER COLUMN syntax")
    2029
    2030        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2032    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2033        if not self.RENAME_TABLE_WITH_DB:
    2034            # Remove db from tables
    2035            expression = expression.transform(
    2036                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2037            )
    2038        this = self.sql(expression, "this")
    2039        return f"RENAME TO {this}"

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

    2041    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2042        actions = expression.args["actions"]
    2043
    2044        if isinstance(actions[0], exp.ColumnDef):
    2045            actions = self.expressions(expression, key="actions", prefix="ADD COLUMN ")
    2046        elif isinstance(actions[0], exp.Schema):
    2047            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2048        elif isinstance(actions[0], exp.Delete):
    2049            actions = self.expressions(expression, key="actions", flat=True)
    2050        else:
    2051            actions = self.expressions(expression, key="actions")
    2052
    2053        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2054        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"

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

    2056    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2057        expressions = self.expressions(expression)
    2058        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2059        return f"DROP{exists}{expressions}"

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

    2061    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2062        this = self.sql(expression, "this")
    2063        expression_ = self.sql(expression, "expression")
    2064        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2065
    2066        enforced = expression.args.get("enforced")
    2067        if enforced is not None:
    2068            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2069
    2070        return f"{add_constraint} {expression_}"

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

    2072    def distinct_sql(self, expression: exp.Distinct) -> str:
    2073        this = self.expressions(expression, flat=True)
    2074        this = f" {this}" if this else ""
    2075
    2076        on = self.sql(expression, "on")
    2077        on = f" ON {on}" if on else ""
    2078        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2086    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2087        return self.sql(
    2088            exp.Cast(
    2089                this=exp.Div(this=expression.this, expression=expression.expression),
    2090                to=exp.DataType(this=exp.DataType.Type.INT),
    2091            )
    2092        )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2175    def use_sql(self, expression: exp.Use) -> str:
    2176        kind = self.sql(expression, "kind")
    2177        kind = f" {kind}" if kind else ""
    2178        this = self.sql(expression, "this")
    2179        this = f" {this}" if this else ""
    2180        return f"USE{kind}{this}"

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

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

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

    2186    def function_fallback_sql(self, expression: exp.Func) -> str:
    2187        args = []
    2188        for arg_value in expression.args.values():
    2189            if isinstance(arg_value, list):
    2190                for value in arg_value:
    2191                    args.append(value)
    2192            else:
    2193                args.append(arg_value)
    2194
    2195        return self.func(expression.sql_name(), *args)

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

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

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

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

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

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

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

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

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

    2212    def expressions(
    2213        self,
    2214        expression: t.Optional[exp.Expression] = None,
    2215        key: t.Optional[str] = None,
    2216        sqls: t.Optional[t.List[str]] = None,
    2217        flat: bool = False,
    2218        indent: bool = True,
    2219        sep: str = ", ",
    2220        prefix: str = "",
    2221    ) -> str:
    2222        expressions = expression.args.get(key or "expressions") if expression else sqls
    2223
    2224        if not expressions:
    2225            return ""
    2226
    2227        if flat:
    2228            return sep.join(self.sql(e) for e in expressions)
    2229
    2230        num_sqls = len(expressions)
    2231
    2232        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2233        pad = " " * self.pad
    2234        stripped_sep = sep.strip()
    2235
    2236        result_sqls = []
    2237        for i, e in enumerate(expressions):
    2238            sql = self.sql(e, comment=False)
    2239            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2240
    2241            if self.pretty:
    2242                if self._leading_comma:
    2243                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2244                else:
    2245                    result_sqls.append(
    2246                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2247                    )
    2248            else:
    2249                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2250
    2251        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2252        return self.indent(result_sql, skip_first=False) if indent else result_sql

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

    2254    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2255        flat = flat or isinstance(expression.parent, exp.Properties)
    2256        expressions_sql = self.expressions(expression, flat=flat)
    2257        if flat:
    2258            return f"{op} {expressions_sql}"
    2259        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    2261    def naked_property(self, expression: exp.Property) -> str:
    2262        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2263        if not property_name:
    2264            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2265        return f"{property_name} {self.sql(expression, 'this')}"

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

    2267    def set_operation(self, expression: exp.Expression, op: str) -> str:
    2268        this = self.sql(expression, "this")
    2269        op = self.seg(op)
    2270        return self.query_modifiers(
    2271            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2272        )

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

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

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

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

    2280    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2281        this = self.sql(expression, "this")
    2282        expressions = self.no_identify(self.expressions, expression)
    2283        expressions = (
    2284            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2285        )
    2286        return f"{this}{expressions}"

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

    2288    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2289        this = self.sql(expression, "this")
    2290        expressions = self.expressions(expression, flat=True)
    2291        return f"{this}({expressions})"

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

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

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

    2296    def when_sql(self, expression: exp.When) -> str:
    2297        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2298        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2299        condition = self.sql(expression, "condition")
    2300        condition = f" AND {condition}" if condition else ""
    2301
    2302        then_expression = expression.args.get("then")
    2303        if isinstance(then_expression, exp.Insert):
    2304            then = f"INSERT {self.sql(then_expression, 'this')}"
    2305            if "expression" in then_expression.args:
    2306                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2307        elif isinstance(then_expression, exp.Update):
    2308            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2309                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2310            else:
    2311                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2312        else:
    2313            then = self.sql(then_expression)
    2314        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    2316    def merge_sql(self, expression: exp.Merge) -> str:
    2317        this = self.sql(expression, "this")
    2318        using = f"USING {self.sql(expression, 'using')}"
    2319        on = f"ON {self.sql(expression, 'on')}"
    2320        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"

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

    2322    def tochar_sql(self, expression: exp.ToChar) -> str:
    2323        if expression.args.get("format"):
    2324            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2325
    2326        return self.sql(exp.cast(expression.this, "text"))

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

    2328    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    2329        this = self.sql(expression, "this")
    2330        kind = self.sql(expression, "kind")
    2331        settings_sql = self.expressions(expression, key="settings", sep=" ")
    2332        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    2333        return f"{this}({kind}{args})"

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

    2335    def dictrange_sql(self, expression: exp.DictRange) -> str:
    2336        this = self.sql(expression, "this")
    2337        max = self.sql(expression, "max")
    2338        min = self.sql(expression, "min")
    2339        return f"{this}(MIN {min} MAX {max})"

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

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

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

    2345def cached_generator(
    2346    cache: t.Optional[t.Dict[int, str]] = None
    2347) -> t.Callable[[exp.Expression], str]:
    2348    """Returns a cached generator."""
    2349    cache = {} if cache is None else cache
    2350    generator = Generator(normalize=True, identify="safe")
    2351    return lambda e: generator.generate(e, cache)

Returns a cached generator.