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

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
- 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, 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

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

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

    354    def generate(
    355        self,
    356        expression: t.Optional[exp.Expression],
    357        cache: t.Optional[t.Dict[int, str]] = None,
    358    ) -> str:
    359        """
    360        Generates a SQL string by interpreting the given syntax tree.
    361
    362        Args
    363            expression: the syntax tree.
    364            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.
    365
    366        Returns
    367            the SQL string.
    368        """
    369        if cache is not None:
    370            self._cache = cache
    371        self.unsupported_messages = []
    372        sql = self.sql(expression).strip()
    373        self._cache = None
    374
    375        if self.unsupported_level == ErrorLevel.IGNORE:
    376            return sql
    377
    378        if self.unsupported_level == ErrorLevel.WARN:
    379            for msg in self.unsupported_messages:
    380                logger.warning(msg)
    381        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    382            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    383
    384        if self.pretty:
    385            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    386        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

    388    def unsupported(self, message: str) -> None:
    389        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    390            raise UnsupportedError(message)
    391        self.unsupported_messages.append(message)

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

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

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

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

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

    399    def pad_comment(self, comment: str) -> str:
    400        comment = " " + comment if comment[0].strip() else comment
    401        comment = comment + " " if comment[-1].strip() else comment
    402        return comment

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

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

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

    432    def wrap(self, expression: exp.Expression | str) -> str:
    433        this_sql = self.indent(
    434            self.sql(expression)
    435            if isinstance(expression, (exp.Select, exp.Union))
    436            else self.sql(expression, "this"),
    437            level=1,
    438            pad=0,
    439        )
    440        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    442    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    443        original = self.identify
    444        self.identify = False
    445        result = func(*args, **kwargs)
    446        self.identify = original
    447        return result

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

    449    def normalize_func(self, name: str) -> str:
    450        if self.normalize_functions == "upper":
    451            return name.upper()
    452        if self.normalize_functions == "lower":
    453            return name.lower()
    454        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

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

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

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

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

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

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

    529    def cache_sql(self, expression: exp.Cache) -> str:
    530        lazy = " LAZY" if expression.args.get("lazy") else ""
    531        table = self.sql(expression, "this")
    532        options = expression.args.get("options")
    533        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    534        sql = self.sql(expression, "expression")
    535        sql = f" AS{self.sep()}{sql}" if sql else ""
    536        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    537        return self.prepend_ctes(expression, sql)

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

    539    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    540        if isinstance(expression.parent, exp.Cast):
    541            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    542        default = "DEFAULT " if expression.args.get("default") else ""
    543        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    545    def column_sql(self, expression: exp.Column) -> str:
    546        return ".".join(
    547            self.sql(part)
    548            for part in (
    549                expression.args.get("catalog"),
    550                expression.args.get("db"),
    551                expression.args.get("table"),
    552                expression.args.get("this"),
    553            )
    554            if part
    555        )

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

    557    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    558        this = self.sql(expression, "this")
    559        this = f" {this}" if this else ""
    560        position = self.sql(expression, "position")
    561        return f"{position}{this}"

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

    563    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    564        column = self.sql(expression, "this")
    565        kind = self.sql(expression, "kind")
    566        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    567        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    568        kind = f"{sep}{kind}" if kind else ""
    569        constraints = f" {constraints}" if constraints else ""
    570        position = self.sql(expression, "position")
    571        position = f" {position}" if position else ""
    572
    573        return f"{exists}{column}{kind}{constraints}{position}"

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

    575    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    576        this = self.sql(expression, "this")
    577        kind_sql = self.sql(expression, "kind").strip()
    578        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

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

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

    583    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    584        if isinstance(expression.this, list):
    585            this = self.wrap(self.expressions(expression, key="this", flat=True))
    586        else:
    587            this = self.sql(expression, "this")
    588
    589        return f"COMPRESS {this}"

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

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

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

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

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

    627    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    628        desc = expression.args.get("desc")
    629        if desc is not None:
    630            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    631        return f"PRIMARY KEY"

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

    633    def uniquecolumnconstraint_sql(self, _) -> str:
    634        return "UNIQUE"

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

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

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

    730    def clone_sql(self, expression: exp.Clone) -> str:
    731        this = self.sql(expression, "this")
    732        when = self.sql(expression, "when")
    733
    734        if when:
    735            kind = self.sql(expression, "kind")
    736            expr = self.sql(expression, "expression")
    737            return f"CLONE {this} {when} ({kind} => {expr})"
    738
    739        return f"CLONE {this}"

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

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

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

    744    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    745        with_ = self.sql(expression, "with")
    746        if with_:
    747            sql = f"{with_}{self.sep()}{sql}"
    748        return sql

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

    750    def with_sql(self, expression: exp.With) -> str:
    751        sql = self.expressions(expression, flat=True)
    752        recursive = "RECURSIVE " if expression.args.get("recursive") else ""
    753
    754        return f"WITH {recursive}{sql}"

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

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

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

    760    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    761        alias = self.sql(expression, "this")
    762        columns = self.expressions(expression, key="columns", flat=True)
    763        columns = f"({columns})" if columns else ""
    764        return f"{alias}{columns}"

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

    766    def bitstring_sql(self, expression: exp.BitString) -> str:
    767        this = self.sql(expression, "this")
    768        if self.bit_start:
    769            return f"{self.bit_start}{this}{self.bit_end}"
    770        return f"{int(this, 2)}"

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

    772    def hexstring_sql(self, expression: exp.HexString) -> str:
    773        this = self.sql(expression, "this")
    774        if self.hex_start:
    775            return f"{self.hex_start}{this}{self.hex_end}"
    776        return f"{int(this, 16)}"

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

    778    def bytestring_sql(self, expression: exp.ByteString) -> str:
    779        this = self.sql(expression, "this")
    780        if self.byte_start:
    781            return f"{self.byte_start}{this}{self.byte_end}"
    782        return this

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

    784    def rawstring_sql(self, expression: exp.RawString) -> str:
    785        if self.raw_start:
    786            return f"{self.raw_start}{expression.name}{self.raw_end}"
    787        return self.sql(exp.Literal.string(expression.name.replace("\\", "\\\\")))

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

    789    def datatypesize_sql(self, expression: exp.DataTypeSize) -> str:
    790        this = self.sql(expression, "this")
    791        specifier = self.sql(expression, "expression")
    792        specifier = f" {specifier}" if specifier else ""
    793        return f"{this}{specifier}"

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

    795    def datatype_sql(self, expression: exp.DataType) -> str:
    796        type_value = expression.this
    797        type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
    798        nested = ""
    799        interior = self.expressions(expression, flat=True)
    800        values = ""
    801        if interior:
    802            if expression.args.get("nested"):
    803                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    804                if expression.args.get("values") is not None:
    805                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    806                    values = f"{delimiters[0]}{self.expressions(expression, key='values')}{delimiters[1]}"
    807            else:
    808                nested = f"({interior})"
    809
    810        return f"{type_sql}{nested}{values}"

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

    812    def directory_sql(self, expression: exp.Directory) -> str:
    813        local = "LOCAL " if expression.args.get("local") else ""
    814        row_format = self.sql(expression, "row_format")
    815        row_format = f" {row_format}" if row_format else ""
    816        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    818    def delete_sql(self, expression: exp.Delete) -> str:
    819        this = self.sql(expression, "this")
    820        this = f" FROM {this}" if this else ""
    821        using_sql = (
    822            f" USING {self.expressions(expression, key='using', sep=', USING ')}"
    823            if expression.args.get("using")
    824            else ""
    825        )
    826        where_sql = self.sql(expression, "where")
    827        returning = self.sql(expression, "returning")
    828        sql = f"DELETE{this}{using_sql}{where_sql}{returning}"
    829        return self.prepend_ctes(expression, sql)

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

    831    def drop_sql(self, expression: exp.Drop) -> str:
    832        this = self.sql(expression, "this")
    833        kind = expression.args["kind"]
    834        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    835        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    836        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    837        cascade = " CASCADE" if expression.args.get("cascade") else ""
    838        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    839        purge = " PURGE" if expression.args.get("purge") else ""
    840        return (
    841            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
    842        )

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

    844    def except_sql(self, expression: exp.Except) -> str:
    845        return self.prepend_ctes(
    846            expression,
    847            self.set_operation(expression, self.except_op(expression)),
    848        )

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

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

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

    853    def fetch_sql(self, expression: exp.Fetch) -> str:
    854        direction = expression.args.get("direction")
    855        direction = f" {direction.upper()}" if direction else ""
    856        count = expression.args.get("count")
    857        count = f" {count}" if count else ""
    858        if expression.args.get("percent"):
    859            count = f"{count} PERCENT"
    860        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    861        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

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

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

    868    def hint_sql(self, expression: exp.Hint) -> str:
    869        if self.sql(expression, "this"):
    870            self.unsupported("Hints are not supported")
    871        return ""

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

    873    def index_sql(self, expression: exp.Index) -> str:
    874        unique = "UNIQUE " if expression.args.get("unique") else ""
    875        primary = "PRIMARY " if expression.args.get("primary") else ""
    876        amp = "AMP " if expression.args.get("amp") else ""
    877        name = f"{expression.name} " if expression.name else ""
    878        table = self.sql(expression, "table")
    879        table = f"{self.INDEX_ON} {table} " if table else ""
    880        index = "INDEX " if not table else ""
    881        columns = self.expressions(expression, key="columns", flat=True)
    882        return f"{unique}{primary}{amp}{index}{name}{table}({columns})"

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

    884    def identifier_sql(self, expression: exp.Identifier) -> str:
    885        text = expression.name
    886        lower = text.lower()
    887        text = lower if self.normalize and not expression.quoted else text
    888        text = text.replace(self.identifier_end, self._escaped_identifier_end)
    889        if (
    890            expression.quoted
    891            or should_identify(text, self.identify)
    892            or lower in self.RESERVED_KEYWORDS
    893        ):
    894            text = f"{self.identifier_start}{text}{self.identifier_end}"
    895        return text

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

    897    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    898        input_format = self.sql(expression, "input_format")
    899        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    900        output_format = self.sql(expression, "output_format")
    901        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    902        return self.sep().join((input_format, output_format))

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

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

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

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

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

    911    def properties_sql(self, expression: exp.Properties) -> str:
    912        root_properties = []
    913        with_properties = []
    914
    915        for p in expression.expressions:
    916            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    917            if p_loc == exp.Properties.Location.POST_WITH:
    918                with_properties.append(p)
    919            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    920                root_properties.append(p)
    921
    922        return self.root_properties(
    923            exp.Properties(expressions=root_properties)
    924        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    926    def root_properties(self, properties: exp.Properties) -> str:
    927        if properties.expressions:
    928            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    929        return ""

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

    931    def properties(
    932        self,
    933        properties: exp.Properties,
    934        prefix: str = "",
    935        sep: str = ", ",
    936        suffix: str = "",
    937        wrapped: bool = True,
    938    ) -> str:
    939        if properties.expressions:
    940            expressions = self.expressions(properties, sep=sep, indent=False)
    941            expressions = self.wrap(expressions) if wrapped else expressions
    942            return f"{prefix}{' ' if prefix and prefix != ' ' else ''}{expressions}{suffix}"
    943        return ""

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

    945    def with_properties(self, properties: exp.Properties) -> str:
    946        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

    948    def locate_properties(
    949        self, properties: exp.Properties
    950    ) -> t.Dict[exp.Properties.Location, list[exp.Property]]:
    951        properties_locs: t.Dict[exp.Properties.Location, list[exp.Property]] = {
    952            key: [] for key in exp.Properties.Location
    953        }
    954
    955        for p in properties.expressions:
    956            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    957            if p_loc == exp.Properties.Location.POST_NAME:
    958                properties_locs[exp.Properties.Location.POST_NAME].append(p)
    959            elif p_loc == exp.Properties.Location.POST_INDEX:
    960                properties_locs[exp.Properties.Location.POST_INDEX].append(p)
    961            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    962                properties_locs[exp.Properties.Location.POST_SCHEMA].append(p)
    963            elif p_loc == exp.Properties.Location.POST_WITH:
    964                properties_locs[exp.Properties.Location.POST_WITH].append(p)
    965            elif p_loc == exp.Properties.Location.POST_CREATE:
    966                properties_locs[exp.Properties.Location.POST_CREATE].append(p)
    967            elif p_loc == exp.Properties.Location.POST_ALIAS:
    968                properties_locs[exp.Properties.Location.POST_ALIAS].append(p)
    969            elif p_loc == exp.Properties.Location.POST_EXPRESSION:
    970                properties_locs[exp.Properties.Location.POST_EXPRESSION].append(p)
    971            elif p_loc == exp.Properties.Location.UNSUPPORTED:
    972                self.unsupported(f"Unsupported property {p.key}")
    973
    974        return properties_locs

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

    976    def property_sql(self, expression: exp.Property) -> str:
    977        property_cls = expression.__class__
    978        if property_cls == exp.Property:
    979            return f"{expression.name}={self.sql(expression, 'value')}"
    980
    981        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    982        if not property_name:
    983            self.unsupported(f"Unsupported property {expression.key}")
    984
    985        return f"{property_name}={self.sql(expression, 'this')}"

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

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

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

    992    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    993        no = "NO " if expression.args.get("no") else ""
    994        protection = " PROTECTION" if expression.args.get("protection") else ""
    995        return f"{no}FALLBACK{protection}"

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

     997    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
     998        no = "NO " if expression.args.get("no") else ""
     999        local = expression.args.get("local")
    1000        local = f"{local} " if local else ""
    1001        dual = "DUAL " if expression.args.get("dual") else ""
    1002        before = "BEFORE " if expression.args.get("before") else ""
    1003        after = "AFTER " if expression.args.get("after") else ""
    1004        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1006    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1007        freespace = self.sql(expression, "this")
    1008        percent = " PERCENT" if expression.args.get("percent") else ""
    1009        return f"FREESPACE={freespace}{percent}"

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

    1011    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1012        if expression.args.get("default"):
    1013            property = "DEFAULT"
    1014        elif expression.args.get("on"):
    1015            property = "ON"
    1016        else:
    1017            property = "OFF"
    1018        return f"CHECKSUM={property}"

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

    1020    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1021        if expression.args.get("no"):
    1022            return "NO MERGEBLOCKRATIO"
    1023        if expression.args.get("default"):
    1024            return "DEFAULT MERGEBLOCKRATIO"
    1025
    1026        percent = " PERCENT" if expression.args.get("percent") else ""
    1027        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1029    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1030        default = expression.args.get("default")
    1031        minimum = expression.args.get("minimum")
    1032        maximum = expression.args.get("maximum")
    1033        if default or minimum or maximum:
    1034            if default:
    1035                prop = "DEFAULT"
    1036            elif minimum:
    1037                prop = "MINIMUM"
    1038            else:
    1039                prop = "MAXIMUM"
    1040            return f"{prop} DATABLOCKSIZE"
    1041        units = expression.args.get("units")
    1042        units = f" {units}" if units else ""
    1043        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1045    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1046        autotemp = expression.args.get("autotemp")
    1047        always = expression.args.get("always")
    1048        default = expression.args.get("default")
    1049        manual = expression.args.get("manual")
    1050        never = expression.args.get("never")
    1051
    1052        if autotemp is not None:
    1053            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1054        elif always:
    1055            prop = "ALWAYS"
    1056        elif default:
    1057            prop = "DEFAULT"
    1058        elif manual:
    1059            prop = "MANUAL"
    1060        elif never:
    1061            prop = "NEVER"
    1062        return f"BLOCKCOMPRESSION={prop}"

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

    1064    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1065        no = expression.args.get("no")
    1066        no = " NO" if no else ""
    1067        concurrent = expression.args.get("concurrent")
    1068        concurrent = " CONCURRENT" if concurrent else ""
    1069
    1070        for_ = ""
    1071        if expression.args.get("for_all"):
    1072            for_ = " FOR ALL"
    1073        elif expression.args.get("for_insert"):
    1074            for_ = " FOR INSERT"
    1075        elif expression.args.get("for_none"):
    1076            for_ = " FOR NONE"
    1077        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

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

    1079    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1080        kind = expression.args.get("kind")
    1081        this: str = f" {this}" if expression.this else ""
    1082        for_or_in = expression.args.get("for_or_in")
    1083        lock_type = expression.args.get("lock_type")
    1084        override = " OVERRIDE" if expression.args.get("override") else ""
    1085        return f"LOCKING {kind}{this} {for_or_in} {lock_type}{override}"

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

    1087    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1088        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1089        statistics = expression.args.get("statistics")
    1090        statistics_sql = ""
    1091        if statistics is not None:
    1092            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1093        return f"{data_sql}{statistics_sql}"

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

    1095    def insert_sql(self, expression: exp.Insert) -> str:
    1096        overwrite = expression.args.get("overwrite")
    1097
    1098        if isinstance(expression.this, exp.Directory):
    1099            this = "OVERWRITE " if overwrite else "INTO "
    1100        else:
    1101            this = "OVERWRITE TABLE " if overwrite else "INTO "
    1102
    1103        alternative = expression.args.get("alternative")
    1104        alternative = f" OR {alternative} " if alternative else " "
    1105        this = f"{this}{self.sql(expression, 'this')}"
    1106
    1107        exists = " IF EXISTS " if expression.args.get("exists") else " "
    1108        partition_sql = (
    1109            self.sql(expression, "partition") if expression.args.get("partition") else ""
    1110        )
    1111        expression_sql = self.sql(expression, "expression")
    1112        conflict = self.sql(expression, "conflict")
    1113        returning = self.sql(expression, "returning")
    1114        sep = self.sep() if partition_sql else ""
    1115        sql = f"INSERT{alternative}{this}{exists}{partition_sql}{sep}{expression_sql}{conflict}{returning}"
    1116        return self.prepend_ctes(expression, sql)

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

    1118    def intersect_sql(self, expression: exp.Intersect) -> str:
    1119        return self.prepend_ctes(
    1120            expression,
    1121            self.set_operation(expression, self.intersect_op(expression)),
    1122        )

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

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

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

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

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

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

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

    1133    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1134        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1135        constraint = self.sql(expression, "constraint")
    1136        if constraint:
    1137            constraint = f"ON CONSTRAINT {constraint}"
    1138        key = self.expressions(expression, key="key", flat=True)
    1139        do = "" if expression.args.get("duplicate") else " DO "
    1140        nothing = "NOTHING" if expression.args.get("nothing") else ""
    1141        expressions = self.expressions(expression, flat=True)
    1142        if expressions:
    1143            expressions = f"UPDATE SET {expressions}"
    1144        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"

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

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

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

    1149    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1150        fields = expression.args.get("fields")
    1151        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1152        escaped = expression.args.get("escaped")
    1153        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1154        items = expression.args.get("collection_items")
    1155        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1156        keys = expression.args.get("map_keys")
    1157        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1158        lines = expression.args.get("lines")
    1159        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1160        null = expression.args.get("null")
    1161        null = f" NULL DEFINED AS {null}" if null else ""
    1162        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

    1164    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1165        table = ".".join(
    1166            part
    1167            for part in [
    1168                self.sql(expression, "catalog"),
    1169                self.sql(expression, "db"),
    1170                self.sql(expression, "this"),
    1171            ]
    1172            if part
    1173        )
    1174
    1175        alias = self.sql(expression, "alias")
    1176        alias = f"{sep}{alias}" if alias else ""
    1177        hints = self.expressions(expression, key="hints", flat=True)
    1178        hints = f" WITH ({hints})" if hints and self.TABLE_HINTS else ""
    1179        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1180        pivots = f" {pivots}" if pivots else ""
    1181        joins = self.expressions(expression, key="joins", sep="")
    1182        laterals = self.expressions(expression, key="laterals", sep="")
    1183        system_time = expression.args.get("system_time")
    1184        system_time = f" {self.sql(expression, 'system_time')}" if system_time else ""
    1185
    1186        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

    1188    def tablesample_sql(
    1189        self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS "
    1190    ) -> str:
    1191        if self.alias_post_tablesample and expression.this.alias:
    1192            table = expression.this.copy()
    1193            table.set("alias", None)
    1194            this = self.sql(table)
    1195            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1196        else:
    1197            this = self.sql(expression, "this")
    1198            alias = ""
    1199        method = self.sql(expression, "method")
    1200        method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1201        numerator = self.sql(expression, "bucket_numerator")
    1202        denominator = self.sql(expression, "bucket_denominator")
    1203        field = self.sql(expression, "bucket_field")
    1204        field = f" ON {field}" if field else ""
    1205        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1206        percent = self.sql(expression, "percent")
    1207        percent = f"{percent} PERCENT" if percent else ""
    1208        rows = self.sql(expression, "rows")
    1209        rows = f"{rows} ROWS" if rows else ""
    1210        size = self.sql(expression, "size")
    1211        if size and self.TABLESAMPLE_SIZE_IS_PERCENT:
    1212            size = f"{size} PERCENT"
    1213        seed = self.sql(expression, "seed")
    1214        seed = f" {seed_prefix} ({seed})" if seed else ""
    1215        kind = expression.args.get("kind", "TABLESAMPLE")
    1216        return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}"

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

    1218    def pivot_sql(self, expression: exp.Pivot) -> str:
    1219        alias = self.sql(expression, "alias")
    1220        alias = f" AS {alias}" if alias else ""
    1221        unpivot = expression.args.get("unpivot")
    1222        direction = "UNPIVOT" if unpivot else "PIVOT"
    1223        expressions = self.expressions(expression, flat=True)
    1224        field = self.sql(expression, "field")
    1225        return f"{direction}({expressions} FOR {field}){alias}"

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

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

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

    1230    def update_sql(self, expression: exp.Update) -> str:
    1231        this = self.sql(expression, "this")
    1232        set_sql = self.expressions(expression, flat=True)
    1233        from_sql = self.sql(expression, "from")
    1234        where_sql = self.sql(expression, "where")
    1235        returning = self.sql(expression, "returning")
    1236        sql = f"UPDATE {this} SET {set_sql}{from_sql}{where_sql}{returning}"
    1237        return self.prepend_ctes(expression, sql)

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

    1239    def values_sql(self, expression: exp.Values) -> str:
    1240        args = self.expressions(expression)
    1241        alias = self.sql(expression, "alias")
    1242        values = f"VALUES{self.seg('')}{args}"
    1243        values = (
    1244            f"({values})"
    1245            if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1246            else values
    1247        )
    1248        return f"{values} AS {alias}" if alias else values

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

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

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

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

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

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

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

    1261    def group_sql(self, expression: exp.Group) -> str:
    1262        group_by = self.op_expressions("GROUP BY", expression)
    1263        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1264        grouping_sets = (
    1265            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1266        )
    1267
    1268        cube = expression.args.get("cube", [])
    1269        if seq_get(cube, 0) is True:
    1270            return f"{group_by}{self.seg('WITH CUBE')}"
    1271        else:
    1272            cube_sql = self.expressions(expression, key="cube", indent=False)
    1273            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1274
    1275        rollup = expression.args.get("rollup", [])
    1276        if seq_get(rollup, 0) is True:
    1277            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1278        else:
    1279            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1280            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1281
    1282        groupings = csv(
    1283            grouping_sets,
    1284            cube_sql,
    1285            rollup_sql,
    1286            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1287            sep=self.GROUPINGS_SEP,
    1288        )
    1289
    1290        if expression.args.get("expressions") and groupings:
    1291            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1292
    1293        return f"{group_by}{groupings}"

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

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

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

    1299    def join_sql(self, expression: exp.Join) -> str:
    1300        op_sql = " ".join(
    1301            op
    1302            for op in (
    1303                "NATURAL" if expression.args.get("natural") else None,
    1304                "GLOBAL" if expression.args.get("global") else None,
    1305                expression.side,
    1306                expression.kind,
    1307                expression.hint if self.JOIN_HINTS else None,
    1308            )
    1309            if op
    1310        )
    1311        on_sql = self.sql(expression, "on")
    1312        using = expression.args.get("using")
    1313
    1314        if not on_sql and using:
    1315            on_sql = csv(*(self.sql(column) for column in using))
    1316
    1317        this_sql = self.sql(expression, "this")
    1318
    1319        if on_sql:
    1320            on_sql = self.indent(on_sql, skip_first=True)
    1321            space = self.seg(" " * self.pad) if self.pretty else " "
    1322            if using:
    1323                on_sql = f"{space}USING ({on_sql})"
    1324            else:
    1325                on_sql = f"{space}ON {on_sql}"
    1326        elif not op_sql:
    1327            return f", {this_sql}"
    1328
    1329        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1330        return f"{self.seg(op_sql)} {this_sql}{on_sql}"

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

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

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

    1337    def lateral_sql(self, expression: exp.Lateral) -> str:
    1338        this = self.sql(expression, "this")
    1339
    1340        if isinstance(expression.this, exp.Subquery):
    1341            return f"LATERAL {this}"
    1342
    1343        if expression.args.get("view"):
    1344            alias = expression.args["alias"]
    1345            columns = self.expressions(alias, key="columns", flat=True)
    1346            table = f" {alias.name}" if alias.name else ""
    1347            columns = f" AS {columns}" if columns else ""
    1348            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1349            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1350
    1351        alias = self.sql(expression, "alias")
    1352        alias = f" AS {alias}" if alias else ""
    1353        return f"LATERAL {this}{alias}"

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

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

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

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

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

    1363    def setitem_sql(self, expression: exp.SetItem) -> str:
    1364        kind = self.sql(expression, "kind")
    1365        kind = f"{kind} " if kind else ""
    1366        this = self.sql(expression, "this")
    1367        expressions = self.expressions(expression)
    1368        collate = self.sql(expression, "collate")
    1369        collate = f" COLLATE {collate}" if collate else ""
    1370        global_ = "GLOBAL " if expression.args.get("global") else ""
    1371        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1373    def set_sql(self, expression: exp.Set) -> str:
    1374        expressions = (
    1375            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1376        )
    1377        return f"SET{expressions}"

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

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

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

    1382    def lock_sql(self, expression: exp.Lock) -> str:
    1383        if not self.LOCKING_READS_SUPPORTED:
    1384            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1385            return ""
    1386
    1387        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1388        expressions = self.expressions(expression, flat=True)
    1389        expressions = f" OF {expressions}" if expressions else ""
    1390        wait = expression.args.get("wait")
    1391
    1392        if wait is not None:
    1393            if isinstance(wait, exp.Literal):
    1394                wait = f" WAIT {self.sql(wait)}"
    1395            else:
    1396                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1397
    1398        return f"{lock_type}{expressions}{wait or ''}"

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

    1400    def literal_sql(self, expression: exp.Literal) -> str:
    1401        text = expression.this or ""
    1402        if expression.is_string:
    1403            text = text.replace(self.quote_end, self._escaped_quote_end)
    1404            if self.pretty:
    1405                text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1406            text = f"{self.quote_start}{text}{self.quote_end}"
    1407        return text

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

    1409    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1410        local = " LOCAL" if expression.args.get("local") else ""
    1411        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1412        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1413        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1414        partition = self.sql(expression, "partition")
    1415        partition = f" {partition}" if partition else ""
    1416        input_format = self.sql(expression, "input_format")
    1417        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1418        serde = self.sql(expression, "serde")
    1419        serde = f" SERDE {serde}" if serde else ""
    1420        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1422    def null_sql(self, *_) -> str:
    1423        return "NULL"

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

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

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

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

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

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

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

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

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

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

    1442    def ordered_sql(self, expression: exp.Ordered) -> str:
    1443        desc = expression.args.get("desc")
    1444        asc = not desc
    1445
    1446        nulls_first = expression.args.get("nulls_first")
    1447        nulls_last = not nulls_first
    1448        nulls_are_large = self.null_ordering == "nulls_are_large"
    1449        nulls_are_small = self.null_ordering == "nulls_are_small"
    1450        nulls_are_last = self.null_ordering == "nulls_are_last"
    1451
    1452        sort_order = " DESC" if desc else ""
    1453        nulls_sort_change = ""
    1454        if nulls_first and (
    1455            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    1456        ):
    1457            nulls_sort_change = " NULLS FIRST"
    1458        elif (
    1459            nulls_last
    1460            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    1461            and not nulls_are_last
    1462        ):
    1463            nulls_sort_change = " NULLS LAST"
    1464
    1465        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    1466            self.unsupported(
    1467                "Sorting in an ORDER BY on NULLS FIRST/NULLS LAST is not supported by this dialect"
    1468            )
    1469            nulls_sort_change = ""
    1470
    1471        return f"{self.sql(expression, 'this')}{sort_order}{nulls_sort_change}"

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

    1473    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    1474        partition = self.partition_by_sql(expression)
    1475        order = self.sql(expression, "order")
    1476        measures = self.expressions(expression, key="measures")
    1477        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    1478        rows = self.sql(expression, "rows")
    1479        rows = self.seg(rows) if rows else ""
    1480        after = self.sql(expression, "after")
    1481        after = self.seg(after) if after else ""
    1482        pattern = self.sql(expression, "pattern")
    1483        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    1484        definition_sqls = [
    1485            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    1486            for definition in expression.args.get("define", [])
    1487        ]
    1488        definitions = self.expressions(sqls=definition_sqls)
    1489        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    1490        body = "".join(
    1491            (
    1492                partition,
    1493                order,
    1494                measures,
    1495                rows,
    1496                after,
    1497                pattern,
    1498                define,
    1499            )
    1500        )
    1501        alias = self.sql(expression, "alias")
    1502        alias = f" {alias}" if alias else ""
    1503        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    1505    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    1506        limit = expression.args.get("limit")
    1507
    1508        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    1509            limit = exp.Limit(expression=limit.args.get("count"))
    1510        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    1511            limit = exp.Fetch(direction="FIRST", count=limit.expression)
    1512
    1513        fetch = isinstance(limit, exp.Fetch)
    1514
    1515        return csv(
    1516            *sqls,
    1517            *[self.sql(join) for join in expression.args.get("joins") or []],
    1518            self.sql(expression, "match"),
    1519            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    1520            self.sql(expression, "where"),
    1521            self.sql(expression, "group"),
    1522            self.sql(expression, "having"),
    1523            *self.after_having_modifiers(expression),
    1524            self.sql(expression, "order"),
    1525            self.sql(expression, "offset") if fetch else self.sql(limit),
    1526            self.sql(limit) if fetch else self.sql(expression, "offset"),
    1527            *self.after_limit_modifiers(expression),
    1528            sep="",
    1529        )

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

    1531    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
    1532        return [
    1533            self.sql(expression, "qualify"),
    1534            self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
    1535            if expression.args.get("windows")
    1536            else "",
    1537        ]

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

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

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

    1544    def select_sql(self, expression: exp.Select) -> str:
    1545        hint = self.sql(expression, "hint")
    1546        distinct = self.sql(expression, "distinct")
    1547        distinct = f" {distinct}" if distinct else ""
    1548        kind = expression.args.get("kind")
    1549        kind = f" AS {kind}" if kind else ""
    1550        expressions = self.expressions(expression)
    1551        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    1552        sql = self.query_modifiers(
    1553            expression,
    1554            f"SELECT{hint}{distinct}{kind}{expressions}",
    1555            self.sql(expression, "into", comment=False),
    1556            self.sql(expression, "from", comment=False),
    1557        )
    1558        return self.prepend_ctes(expression, sql)

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

    1560    def schema_sql(self, expression: exp.Schema) -> str:
    1561        this = self.sql(expression, "this")
    1562        this = f"{this} " if this else ""
    1563        sql = f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    1564        return f"{this}{sql}"

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

    1566    def star_sql(self, expression: exp.Star) -> str:
    1567        except_ = self.expressions(expression, key="except", flat=True)
    1568        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    1569        replace = self.expressions(expression, key="replace", flat=True)
    1570        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    1571        return f"*{except_}{replace}"

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

    1573    def parameter_sql(self, expression: exp.Parameter) -> str:
    1574        this = self.sql(expression, "this")
    1575        this = f"{{{this}}}" if expression.args.get("wrapped") else f"{this}"
    1576        return f"{self.PARAMETER_TOKEN}{this}"

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

    1578    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    1579        this = self.sql(expression, "this")
    1580        kind = expression.text("kind")
    1581        if kind:
    1582            kind = f"{kind}."
    1583        return f"@@{kind}{this}"

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

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

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

    1588    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    1589        alias = self.sql(expression, "alias")
    1590        alias = f"{sep}{alias}" if alias else ""
    1591
    1592        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1593        pivots = f" {pivots}" if pivots else ""
    1594
    1595        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    1596        return self.prepend_ctes(expression, sql)

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

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

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

    1602    def union_sql(self, expression: exp.Union) -> str:
    1603        return self.prepend_ctes(
    1604            expression,
    1605            self.set_operation(expression, self.union_op(expression)),
    1606        )

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

    1608    def union_op(self, expression: exp.Union) -> str:
    1609        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    1610        kind = kind if expression.args.get("distinct") else " ALL"
    1611        return f"UNION{kind}"

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

    1613    def unnest_sql(self, expression: exp.Unnest) -> str:
    1614        args = self.expressions(expression, flat=True)
    1615        alias = expression.args.get("alias")
    1616        if alias and self.unnest_column_only:
    1617            columns = alias.columns
    1618            alias = self.sql(columns[0]) if columns else ""
    1619        else:
    1620            alias = self.sql(expression, "alias")
    1621        alias = f" AS {alias}" if alias else alias
    1622        ordinality = " WITH ORDINALITY" if expression.args.get("ordinality") else ""
    1623        offset = expression.args.get("offset")
    1624        offset = f" WITH OFFSET AS {self.sql(offset)}" if offset else ""
    1625        return f"UNNEST({args}){ordinality}{alias}{offset}"

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

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

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

    1631    def window_sql(self, expression: exp.Window) -> str:
    1632        this = self.sql(expression, "this")
    1633
    1634        partition = self.partition_by_sql(expression)
    1635
    1636        order = expression.args.get("order")
    1637        order_sql = self.order_sql(order, flat=True) if order else ""
    1638
    1639        partition_sql = partition + " " if partition and order else partition
    1640
    1641        spec = expression.args.get("spec")
    1642        spec_sql = " " + self.windowspec_sql(spec) if spec else ""
    1643
    1644        alias = self.sql(expression, "alias")
    1645        over = self.sql(expression, "over") or "OVER"
    1646        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    1647
    1648        first = expression.args.get("first")
    1649        if first is not None:
    1650            first = " FIRST " if first else " LAST "
    1651        first = first or ""
    1652
    1653        if not partition and not order and not spec and alias:
    1654            return f"{this} {alias}"
    1655
    1656        window_args = alias + first + partition_sql + order_sql + spec_sql
    1657
    1658        return f"{this} ({window_args.strip()})"

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

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

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

    1664    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    1665        kind = self.sql(expression, "kind")
    1666        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    1667        end = (
    1668            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    1669            or "CURRENT ROW"
    1670        )
    1671        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    1678    def between_sql(self, expression: exp.Between) -> str:
    1679        this = self.sql(expression, "this")
    1680        low = self.sql(expression, "low")
    1681        high = self.sql(expression, "high")
    1682        return f"{this} BETWEEN {low} AND {high}"

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

    1684    def bracket_sql(self, expression: exp.Bracket) -> str:
    1685        expressions = apply_index_offset(expression.this, expression.expressions, self.index_offset)
    1686        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    1687
    1688        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    1693    def any_sql(self, expression: exp.Any) -> str:
    1694        this = self.sql(expression, "this")
    1695        if isinstance(expression.this, exp.Subqueryable):
    1696            this = self.wrap(this)
    1697        return f"ANY {this}"

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

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

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

    1702    def case_sql(self, expression: exp.Case) -> str:
    1703        this = self.sql(expression, "this")
    1704        statements = [f"CASE {this}" if this else "CASE"]
    1705
    1706        for e in expression.args["ifs"]:
    1707            statements.append(f"WHEN {self.sql(e, 'this')}")
    1708            statements.append(f"THEN {self.sql(e, 'true')}")
    1709
    1710        default = self.sql(expression, "default")
    1711
    1712        if default:
    1713            statements.append(f"ELSE {default}")
    1714
    1715        statements.append("END")
    1716
    1717        if self.pretty and self.text_width(statements) > self._max_text_width:
    1718            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    1719
    1720        return " ".join(statements)

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

    1722    def constraint_sql(self, expression: exp.Constraint) -> str:
    1723        this = self.sql(expression, "this")
    1724        expressions = self.expressions(expression, flat=True)
    1725        return f"CONSTRAINT {this} {expressions}"

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

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

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

    1732    def extract_sql(self, expression: exp.Extract) -> str:
    1733        this = self.sql(expression, "this")
    1734        expression_sql = self.sql(expression, "expression")
    1735        return f"EXTRACT({this} FROM {expression_sql})"

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

    1737    def trim_sql(self, expression: exp.Trim) -> str:
    1738        trim_type = self.sql(expression, "position")
    1739
    1740        if trim_type == "LEADING":
    1741            return self.func("LTRIM", expression.this)
    1742        elif trim_type == "TRAILING":
    1743            return self.func("RTRIM", expression.this)
    1744        else:
    1745            return self.func("TRIM", expression.this, expression.expression)

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

    1747    def concat_sql(self, expression: exp.Concat) -> str:
    1748        if len(expression.expressions) == 1:
    1749            return self.sql(expression.expressions[0])
    1750        return self.function_fallback_sql(expression)

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

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

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

    1756    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    1757        expressions = self.expressions(expression, flat=True)
    1758        reference = self.sql(expression, "reference")
    1759        reference = f" {reference}" if reference else ""
    1760        delete = self.sql(expression, "delete")
    1761        delete = f" ON DELETE {delete}" if delete else ""
    1762        update = self.sql(expression, "update")
    1763        update = f" ON UPDATE {update}" if update else ""
    1764        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    1766    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    1767        expressions = self.expressions(expression, flat=True)
    1768        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1769        options = f" {options}" if options else ""
    1770        return f"PRIMARY KEY ({expressions}){options}"

def unique_sql(self, expression: sqlglot.expressions.Unique) -> str: View Source

    1772    def unique_sql(self, expression: exp.Unique) -> str:
    1773        columns = self.expressions(expression, key="expressions")
    1774        return f"UNIQUE ({columns})"

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

    1776    def if_sql(self, expression: exp.If) -> str:
    1777        return self.case_sql(
    1778            exp.Case(ifs=[expression.copy()], default=expression.args.get("false"))
    1779        )

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

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

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

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

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

    1789    def jsonobject_sql(self, expression: exp.JSONObject) -> str:
    1790        expressions = self.expressions(expression)
    1791        null_handling = expression.args.get("null_handling")
    1792        null_handling = f" {null_handling}" if null_handling else ""
    1793        unique_keys = expression.args.get("unique_keys")
    1794        if unique_keys is not None:
    1795            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    1796        else:
    1797            unique_keys = ""
    1798        return_type = self.sql(expression, "return_type")
    1799        return_type = f" RETURNING {return_type}" if return_type else ""
    1800        format_json = " FORMAT JSON" if expression.args.get("format_json") else ""
    1801        encoding = self.sql(expression, "encoding")
    1802        encoding = f" ENCODING {encoding}" if encoding else ""
    1803        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

    1805    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    1806        this = self.sql(expression, "this")
    1807        kind = self.sql(expression, "kind")
    1808        path = self.sql(expression, "path")
    1809        path = f" {path}" if path else ""
    1810        as_json = " AS JSON" if expression.args.get("as_json") else ""
    1811        return f"{this} {kind}{path}{as_json}"

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

    1813    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    1814        this = self.sql(expression, "this")
    1815        path = self.sql(expression, "path")
    1816        path = f", {path}" if path else ""
    1817        expressions = self.expressions(expression)
    1818        with_ = (
    1819            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    1820            if expressions
    1821            else ""
    1822        )
    1823        return f"OPENJSON({this}{path}){with_}"

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

    1825    def in_sql(self, expression: exp.In) -> str:
    1826        query = expression.args.get("query")
    1827        unnest = expression.args.get("unnest")
    1828        field = expression.args.get("field")
    1829        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    1830
    1831        if query:
    1832            in_sql = self.wrap(query)
    1833        elif unnest:
    1834            in_sql = self.in_unnest_op(unnest)
    1835        elif field:
    1836            in_sql = self.sql(field)
    1837        else:
    1838            in_sql = f"({self.expressions(expression, flat=True)})"
    1839
    1840        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    1845    def interval_sql(self, expression: exp.Interval) -> str:
    1846        unit = self.sql(expression, "unit")
    1847        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    1848            unit = self.TIME_PART_SINGULARS.get(unit.lower(), unit)
    1849        unit = f" {unit}" if unit else ""
    1850
    1851        if self.SINGLE_STRING_INTERVAL:
    1852            this = expression.this.name if expression.this else ""
    1853            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    1854
    1855        this = self.sql(expression, "this")
    1856        if this:
    1857            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    1858            this = f" {this}" if unwrapped else f" ({this})"
    1859
    1860        return f"INTERVAL{this}{unit}"

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

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

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

    1865    def reference_sql(self, expression: exp.Reference) -> str:
    1866        this = self.sql(expression, "this")
    1867        expressions = self.expressions(expression, flat=True)
    1868        expressions = f"({expressions})" if expressions else ""
    1869        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1870        options = f" {options}" if options else ""
    1871        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    1876    def paren_sql(self, expression: exp.Paren) -> str:
    1877        if isinstance(expression.unnest(), exp.Select):
    1878            sql = self.wrap(expression)
    1879        else:
    1880            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    1881            sql = f"({sql}{self.seg(')', sep='')}"
    1882
    1883        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    1894    def alias_sql(self, expression: exp.Alias) -> str:
    1895        alias = self.sql(expression, "alias")
    1896        alias = f" AS {alias}" if alias else ""
    1897        return f"{self.sql(expression, 'this')}{alias}"

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

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

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

    1902    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    1903        this = self.sql(expression, "this")
    1904        zone = self.sql(expression, "zone")
    1905        return f"{this} AT TIME ZONE {zone}"

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

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

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

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

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

    1913    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    1914        if not self.pretty:
    1915            return self.binary(expression, op)
    1916
    1917        sqls = tuple(
    1918            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    1919            for i, e in enumerate(expression.flatten(unnest=False))
    1920        )
    1921
    1922        sep = "\n" if self.text_width(sqls) > self._max_text_width else " "
    1923        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1956    def comment_sql(self, expression: exp.Comment) -> str:
    1957        this = self.sql(expression, "this")
    1958        kind = expression.args["kind"]
    1959        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1960        expression_sql = self.sql(expression, "expression")
    1961        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    1963    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    1964        this = self.sql(expression, "this")
    1965        delete = " DELETE" if expression.args.get("delete") else ""
    1966        recompress = self.sql(expression, "recompress")
    1967        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    1968        to_disk = self.sql(expression, "to_disk")
    1969        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    1970        to_volume = self.sql(expression, "to_volume")
    1971        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    1972        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    1974    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    1975        where = self.sql(expression, "where")
    1976        group = self.sql(expression, "group")
    1977        aggregates = self.expressions(expression, key="aggregates")
    1978        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    1979
    1980        if not (where or group or aggregates) and len(expression.expressions) == 1:
    1981            return f"TTL {self.expressions(expression, flat=True)}"
    1982
    1983        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    1988    def commit_sql(self, expression: exp.Commit) -> str:
    1989        chain = expression.args.get("chain")
    1990        if chain is not None:
    1991            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    1992
    1993        return f"COMMIT{chain or ''}"

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

    1995    def rollback_sql(self, expression: exp.Rollback) -> str:
    1996        savepoint = expression.args.get("savepoint")
    1997        savepoint = f" TO {savepoint}" if savepoint else ""
    1998        return f"ROLLBACK{savepoint}"

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

    2000    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2001        this = self.sql(expression, "this")
    2002
    2003        dtype = self.sql(expression, "dtype")
    2004        if dtype:
    2005            collate = self.sql(expression, "collate")
    2006            collate = f" COLLATE {collate}" if collate else ""
    2007            using = self.sql(expression, "using")
    2008            using = f" USING {using}" if using else ""
    2009            return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}"
    2010
    2011        default = self.sql(expression, "default")
    2012        if default:
    2013            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2014
    2015        if not expression.args.get("drop"):
    2016            self.unsupported("Unsupported ALTER COLUMN syntax")
    2017
    2018        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2020    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2021        if not self.RENAME_TABLE_WITH_DB:
    2022            # Remove db from tables
    2023            expression = expression.transform(
    2024                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2025            )
    2026        this = self.sql(expression, "this")
    2027        return f"RENAME TO {this}"

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

    2029    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2030        actions = expression.args["actions"]
    2031
    2032        if isinstance(actions[0], exp.ColumnDef):
    2033            actions = self.expressions(expression, key="actions", prefix="ADD COLUMN ")
    2034        elif isinstance(actions[0], exp.Schema):
    2035            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2036        elif isinstance(actions[0], exp.Delete):
    2037            actions = self.expressions(expression, key="actions", flat=True)
    2038        else:
    2039            actions = self.expressions(expression, key="actions")
    2040
    2041        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2042        return f"ALTER TABLE{exists} {self.sql(expression, 'this')} {actions}"

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

    2044    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2045        expressions = self.expressions(expression)
    2046        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2047        return f"DROP{exists}{expressions}"

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

    2049    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    2050        this = self.sql(expression, "this")
    2051        expression_ = self.sql(expression, "expression")
    2052        add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD"
    2053
    2054        enforced = expression.args.get("enforced")
    2055        if enforced is not None:
    2056            return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}"
    2057
    2058        return f"{add_constraint} {expression_}"

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

    2060    def distinct_sql(self, expression: exp.Distinct) -> str:
    2061        this = self.expressions(expression, flat=True)
    2062        this = f" {this}" if this else ""
    2063
    2064        on = self.sql(expression, "on")
    2065        on = f" ON {on}" if on else ""
    2066        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2074    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2075        return self.sql(
    2076            exp.Cast(
    2077                this=exp.Div(this=expression.this, expression=expression.expression),
    2078                to=exp.DataType(this=exp.DataType.Type.INT),
    2079            )
    2080        )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2163    def use_sql(self, expression: exp.Use) -> str:
    2164        kind = self.sql(expression, "kind")
    2165        kind = f" {kind}" if kind else ""
    2166        this = self.sql(expression, "this")
    2167        this = f" {this}" if this else ""
    2168        return f"USE{kind}{this}"

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

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

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

    2174    def function_fallback_sql(self, expression: exp.Func) -> str:
    2175        args = []
    2176        for arg_value in expression.args.values():
    2177            if isinstance(arg_value, list):
    2178                for value in arg_value:
    2179                    args.append(value)
    2180            else:
    2181                args.append(arg_value)
    2182
    2183        return self.func(expression.sql_name(), *args)

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

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

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

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

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

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

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

    2197    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    2198        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

    2200    def expressions(
    2201        self,
    2202        expression: t.Optional[exp.Expression] = None,
    2203        key: t.Optional[str] = None,
    2204        sqls: t.Optional[t.List[str]] = None,
    2205        flat: bool = False,
    2206        indent: bool = True,
    2207        sep: str = ", ",
    2208        prefix: str = "",
    2209    ) -> str:
    2210        expressions = expression.args.get(key or "expressions") if expression else sqls
    2211
    2212        if not expressions:
    2213            return ""
    2214
    2215        if flat:
    2216            return sep.join(self.sql(e) for e in expressions)
    2217
    2218        num_sqls = len(expressions)
    2219
    2220        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    2221        pad = " " * self.pad
    2222        stripped_sep = sep.strip()
    2223
    2224        result_sqls = []
    2225        for i, e in enumerate(expressions):
    2226            sql = self.sql(e, comment=False)
    2227            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    2228
    2229            if self.pretty:
    2230                if self._leading_comma:
    2231                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    2232                else:
    2233                    result_sqls.append(
    2234                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    2235                    )
    2236            else:
    2237                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    2238
    2239        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    2240        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

    2242    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    2243        flat = flat or isinstance(expression.parent, exp.Properties)
    2244        expressions_sql = self.expressions(expression, flat=flat)
    2245        if flat:
    2246            return f"{op} {expressions_sql}"
    2247        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    2249    def naked_property(self, expression: exp.Property) -> str:
    2250        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    2251        if not property_name:
    2252            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    2253        return f"{property_name} {self.sql(expression, 'this')}"

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

    2255    def set_operation(self, expression: exp.Expression, op: str) -> str:
    2256        this = self.sql(expression, "this")
    2257        op = self.seg(op)
    2258        return self.query_modifiers(
    2259            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
    2260        )

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

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

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

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

    2268    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    2269        this = self.sql(expression, "this")
    2270        expressions = self.no_identify(self.expressions, expression)
    2271        expressions = (
    2272            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    2273        )
    2274        return f"{this}{expressions}"

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

    2276    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    2277        this = self.sql(expression, "this")
    2278        expressions = self.expressions(expression, flat=True)
    2279        return f"{this}({expressions})"

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

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

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

    2284    def when_sql(self, expression: exp.When) -> str:
    2285        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    2286        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    2287        condition = self.sql(expression, "condition")
    2288        condition = f" AND {condition}" if condition else ""
    2289
    2290        then_expression = expression.args.get("then")
    2291        if isinstance(then_expression, exp.Insert):
    2292            then = f"INSERT {self.sql(then_expression, 'this')}"
    2293            if "expression" in then_expression.args:
    2294                then += f" VALUES {self.sql(then_expression, 'expression')}"
    2295        elif isinstance(then_expression, exp.Update):
    2296            if isinstance(then_expression.args.get("expressions"), exp.Star):
    2297                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    2298            else:
    2299                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    2300        else:
    2301            then = self.sql(then_expression)
    2302        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    2304    def merge_sql(self, expression: exp.Merge) -> str:
    2305        this = self.sql(expression, "this")
    2306        using = f"USING {self.sql(expression, 'using')}"
    2307        on = f"ON {self.sql(expression, 'on')}"
    2308        return f"MERGE INTO {this} {using} {on} {self.expressions(expression, sep=' ')}"

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

    2310    def tochar_sql(self, expression: exp.ToChar) -> str:
    2311        if expression.args.get("format"):
    2312            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    2313
    2314        return self.sql(exp.cast(expression.this, "text"))

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

    2317def cached_generator(
    2318    cache: t.Optional[t.Dict[int, str]] = None
    2319) -> t.Callable[[exp.Expression], str]:
    2320    """Returns a cached generator."""
    2321    cache = {} if cache is None else cache
    2322    generator = Generator(normalize=True, identify="safe")
    2323    return lambda e: generator.generate(e, cache)

Returns a cached generator.