Edit on GitHub

# sqlglot.generator

View Source

       1from __future__ import annotations
       2
       3import logging
       4import re
       5import typing as t
       6from collections import defaultdict
       7from functools import reduce, wraps
       8
       9from sqlglot import exp
      10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages
      11from sqlglot.helper import apply_index_offset, csv, name_sequence, seq_get
      12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS
      13from sqlglot.time import format_time
      14from sqlglot.tokens import TokenType
      15
      16if t.TYPE_CHECKING:
      17    from sqlglot._typing import E
      18    from sqlglot.dialects.dialect import DialectType
      19
      20    G = t.TypeVar("G", bound="Generator")
      21    GeneratorMethod = t.Callable[[G, E], str]
      22
      23logger = logging.getLogger("sqlglot")
      24
      25ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)")
      26UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
      27
      28
      29def unsupported_args(
      30    *args: t.Union[str, t.Tuple[str, str]],
      31) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
      32    """
      33    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
      34    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
      35    """
      36    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
      37    for arg in args:
      38        if isinstance(arg, str):
      39            diagnostic_by_arg[arg] = None
      40        else:
      41            diagnostic_by_arg[arg[0]] = arg[1]
      42
      43    def decorator(func: GeneratorMethod) -> GeneratorMethod:
      44        @wraps(func)
      45        def _func(generator: G, expression: E) -> str:
      46            expression_name = expression.__class__.__name__
      47            dialect_name = generator.dialect.__class__.__name__
      48
      49            for arg_name, diagnostic in diagnostic_by_arg.items():
      50                if expression.args.get(arg_name):
      51                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
      52                        arg_name, expression_name, dialect_name
      53                    )
      54                    generator.unsupported(diagnostic)
      55
      56            return func(generator, expression)
      57
      58        return _func
      59
      60    return decorator
      61
      62
      63class _Generator(type):
      64    def __new__(cls, clsname, bases, attrs):
      65        klass = super().__new__(cls, clsname, bases, attrs)
      66
      67        # Remove transforms that correspond to unsupported JSONPathPart expressions
      68        for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS:
      69            klass.TRANSFORMS.pop(part, None)
      70
      71        return klass
      72
      73
      74class Generator(metaclass=_Generator):
      75    """
      76    Generator converts a given syntax tree to the corresponding SQL string.
      77
      78    Args:
      79        pretty: Whether to format the produced SQL string.
      80            Default: False.
      81        identify: Determines when an identifier should be quoted. Possible values are:
      82            False (default): Never quote, except in cases where it's mandatory by the dialect.
      83            True or 'always': Always quote.
      84            'safe': Only quote identifiers that are case insensitive.
      85        normalize: Whether to normalize identifiers to lowercase.
      86            Default: False.
      87        pad: The pad size in a formatted string. For example, this affects the indentation of
      88            a projection in a query, relative to its nesting level.
      89            Default: 2.
      90        indent: The indentation size in a formatted string. For example, this affects the
      91            indentation of subqueries and filters under a `WHERE` clause.
      92            Default: 2.
      93        normalize_functions: How to normalize function names. Possible values are:
      94            "upper" or True (default): Convert names to uppercase.
      95            "lower": Convert names to lowercase.
      96            False: Disables function name normalization.
      97        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
      98            Default ErrorLevel.WARN.
      99        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
     100            This is only relevant if unsupported_level is ErrorLevel.RAISE.
     101            Default: 3
     102        leading_comma: Whether the comma is leading or trailing in select expressions.
     103            This is only relevant when generating in pretty mode.
     104            Default: False
     105        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
     106            The default is on the smaller end because the length only represents a segment and not the true
     107            line length.
     108            Default: 80
     109        comments: Whether to preserve comments in the output SQL code.
     110            Default: True
     111    """
     112
     113    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
     114        **JSON_PATH_PART_TRANSFORMS,
     115        exp.AllowedValuesProperty: lambda self,
     116        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
     117        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
     118        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
     119        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
     120        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
     121        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
     122        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
     123        exp.CaseSpecificColumnConstraint: lambda _,
     124        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
     125        exp.Ceil: lambda self, e: self.ceil_floor(e),
     126        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
     127        exp.CharacterSetProperty: lambda self,
     128        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
     129        exp.ClusteredColumnConstraint: lambda self,
     130        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
     131        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
     132        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
     133        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
     134        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
     135        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
     136        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
     137        exp.DynamicProperty: lambda *_: "DYNAMIC",
     138        exp.EmptyProperty: lambda *_: "EMPTY",
     139        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
     140        exp.EphemeralColumnConstraint: lambda self,
     141        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
     142        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
     143        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
     144        exp.Except: lambda self, e: self.set_operations(e),
     145        exp.ExternalProperty: lambda *_: "EXTERNAL",
     146        exp.Floor: lambda self, e: self.ceil_floor(e),
     147        exp.GlobalProperty: lambda *_: "GLOBAL",
     148        exp.HeapProperty: lambda *_: "HEAP",
     149        exp.IcebergProperty: lambda *_: "ICEBERG",
     150        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
     151        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
     152        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
     153        exp.Intersect: lambda self, e: self.set_operations(e),
     154        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
     155        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
     156        exp.LanguageProperty: lambda self, e: self.naked_property(e),
     157        exp.LocationProperty: lambda self, e: self.naked_property(e),
     158        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
     159        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
     160        exp.NonClusteredColumnConstraint: lambda self,
     161        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
     162        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
     163        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
     164        exp.OnCommitProperty: lambda _,
     165        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
     166        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
     167        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
     168        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
     169        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
     170        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
     171        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
     172        exp.ProjectionPolicyColumnConstraint: lambda self,
     173        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
     174        exp.RemoteWithConnectionModelProperty: lambda self,
     175        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
     176        exp.ReturnsProperty: lambda self, e: (
     177            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
     178        ),
     179        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
     180        exp.SecureProperty: lambda *_: "SECURE",
     181        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
     182        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
     183        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
     184        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
     185        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
     186        exp.SqlReadWriteProperty: lambda _, e: e.name,
     187        exp.SqlSecurityProperty: lambda _,
     188        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
     189        exp.StabilityProperty: lambda _, e: e.name,
     190        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
     191        exp.StreamingTableProperty: lambda *_: "STREAMING",
     192        exp.StrictProperty: lambda *_: "STRICT",
     193        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
     194        exp.TemporaryProperty: lambda *_: "TEMPORARY",
     195        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
     196        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     197        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
     198        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
     199        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
     200        exp.TransientProperty: lambda *_: "TRANSIENT",
     201        exp.Union: lambda self, e: self.set_operations(e),
     202        exp.UnloggedProperty: lambda *_: "UNLOGGED",
     203        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
     204        exp.Uuid: lambda *_: "UUID()",
     205        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
     206        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
     207        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
     208        exp.VolatileProperty: lambda *_: "VOLATILE",
     209        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
     210        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
     211        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
     212        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
     213    }
     214
     215    # Whether null ordering is supported in order by
     216    # True: Full Support, None: No support, False: No support for certain cases
     217    # such as window specifications, aggregate functions etc
     218    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
     219
     220    # Whether ignore nulls is inside the agg or outside.
     221    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
     222    IGNORE_NULLS_IN_FUNC = False
     223
     224    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     225    LOCKING_READS_SUPPORTED = False
     226
     227    # Whether the EXCEPT and INTERSECT operations can return duplicates
     228    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
     229
     230    # Wrap derived values in parens, usually standard but spark doesn't support it
     231    WRAP_DERIVED_VALUES = True
     232
     233    # Whether create function uses an AS before the RETURN
     234    CREATE_FUNCTION_RETURN_AS = True
     235
     236    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
     237    MATCHED_BY_SOURCE = True
     238
     239    # Whether the INTERVAL expression works only with values like '1 day'
     240    SINGLE_STRING_INTERVAL = False
     241
     242    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     243    INTERVAL_ALLOWS_PLURAL_FORM = True
     244
     245    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     246    LIMIT_FETCH = "ALL"
     247
     248    # Whether limit and fetch allows expresions or just limits
     249    LIMIT_ONLY_LITERALS = False
     250
     251    # Whether a table is allowed to be renamed with a db
     252    RENAME_TABLE_WITH_DB = True
     253
     254    # The separator for grouping sets and rollups
     255    GROUPINGS_SEP = ","
     256
     257    # The string used for creating an index on a table
     258    INDEX_ON = "ON"
     259
     260    # Whether join hints should be generated
     261    JOIN_HINTS = True
     262
     263    # Whether table hints should be generated
     264    TABLE_HINTS = True
     265
     266    # Whether query hints should be generated
     267    QUERY_HINTS = True
     268
     269    # What kind of separator to use for query hints
     270    QUERY_HINT_SEP = ", "
     271
     272    # Whether comparing against booleans (e.g. x IS TRUE) is supported
     273    IS_BOOL_ALLOWED = True
     274
     275    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
     276    DUPLICATE_KEY_UPDATE_WITH_SET = True
     277
     278    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
     279    LIMIT_IS_TOP = False
     280
     281    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
     282    RETURNING_END = True
     283
     284    # Whether to generate an unquoted value for EXTRACT's date part argument
     285    EXTRACT_ALLOWS_QUOTES = True
     286
     287    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
     288    TZ_TO_WITH_TIME_ZONE = False
     289
     290    # Whether the NVL2 function is supported
     291    NVL2_SUPPORTED = True
     292
     293    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
     294    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
     295
     296    # Whether VALUES statements can be used as derived tables.
     297    # MySQL 5 and Redshift do not allow this, so when False, it will convert
     298    # SELECT * VALUES into SELECT UNION
     299    VALUES_AS_TABLE = True
     300
     301    # Whether the word COLUMN is included when adding a column with ALTER TABLE
     302    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
     303
     304    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     305    UNNEST_WITH_ORDINALITY = True
     306
     307    # Whether FILTER (WHERE cond) can be used for conditional aggregation
     308    AGGREGATE_FILTER_SUPPORTED = True
     309
     310    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
     311    SEMI_ANTI_JOIN_WITH_SIDE = True
     312
     313    # Whether to include the type of a computed column in the CREATE DDL
     314    COMPUTED_COLUMN_WITH_TYPE = True
     315
     316    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
     317    SUPPORTS_TABLE_COPY = True
     318
     319    # Whether parentheses are required around the table sample's expression
     320    TABLESAMPLE_REQUIRES_PARENS = True
     321
     322    # Whether a table sample clause's size needs to be followed by the ROWS keyword
     323    TABLESAMPLE_SIZE_IS_ROWS = True
     324
     325    # The keyword(s) to use when generating a sample clause
     326    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
     327
     328    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
     329    TABLESAMPLE_WITH_METHOD = True
     330
     331    # The keyword to use when specifying the seed of a sample clause
     332    TABLESAMPLE_SEED_KEYWORD = "SEED"
     333
     334    # Whether COLLATE is a function instead of a binary operator
     335    COLLATE_IS_FUNC = False
     336
     337    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
     338    DATA_TYPE_SPECIFIERS_ALLOWED = False
     339
     340    # Whether conditions require booleans WHERE x = 0 vs WHERE x
     341    ENSURE_BOOLS = False
     342
     343    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
     344    CTE_RECURSIVE_KEYWORD_REQUIRED = True
     345
     346    # Whether CONCAT requires >1 arguments
     347    SUPPORTS_SINGLE_ARG_CONCAT = True
     348
     349    # Whether LAST_DAY function supports a date part argument
     350    LAST_DAY_SUPPORTS_DATE_PART = True
     351
     352    # Whether named columns are allowed in table aliases
     353    SUPPORTS_TABLE_ALIAS_COLUMNS = True
     354
     355    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
     356    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
     357
     358    # What delimiter to use for separating JSON key/value pairs
     359    JSON_KEY_VALUE_PAIR_SEP = ":"
     360
     361    # INSERT OVERWRITE TABLE x override
     362    INSERT_OVERWRITE = " OVERWRITE TABLE"
     363
     364    # Whether the SELECT .. INTO syntax is used instead of CTAS
     365    SUPPORTS_SELECT_INTO = False
     366
     367    # Whether UNLOGGED tables can be created
     368    SUPPORTS_UNLOGGED_TABLES = False
     369
     370    # Whether the CREATE TABLE LIKE statement is supported
     371    SUPPORTS_CREATE_TABLE_LIKE = True
     372
     373    # Whether the LikeProperty needs to be specified inside of the schema clause
     374    LIKE_PROPERTY_INSIDE_SCHEMA = False
     375
     376    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
     377    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
     378    MULTI_ARG_DISTINCT = True
     379
     380    # Whether the JSON extraction operators expect a value of type JSON
     381    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
     382
     383    # Whether bracketed keys like ["foo"] are supported in JSON paths
     384    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
     385
     386    # Whether to escape keys using single quotes in JSON paths
     387    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
     388
     389    # The JSONPathPart expressions supported by this dialect
     390    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
     391
     392    # Whether any(f(x) for x in array) can be implemented by this dialect
     393    CAN_IMPLEMENT_ARRAY_ANY = False
     394
     395    # Whether the function TO_NUMBER is supported
     396    SUPPORTS_TO_NUMBER = True
     397
     398    # Whether or not set op modifiers apply to the outer set op or select.
     399    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
     400    # True means limit 1 happens after the set op, False means it it happens on y.
     401    SET_OP_MODIFIERS = True
     402
     403    # Whether parameters from COPY statement are wrapped in parentheses
     404    COPY_PARAMS_ARE_WRAPPED = True
     405
     406    # Whether values of params are set with "=" token or empty space
     407    COPY_PARAMS_EQ_REQUIRED = False
     408
     409    # Whether COPY statement has INTO keyword
     410    COPY_HAS_INTO_KEYWORD = True
     411
     412    # Whether the conditional TRY(expression) function is supported
     413    TRY_SUPPORTED = True
     414
     415    # Whether the UESCAPE syntax in unicode strings is supported
     416    SUPPORTS_UESCAPE = True
     417
     418    # The keyword to use when generating a star projection with excluded columns
     419    STAR_EXCEPT = "EXCEPT"
     420
     421    # The HEX function name
     422    HEX_FUNC = "HEX"
     423
     424    # The keywords to use when prefixing & separating WITH based properties
     425    WITH_PROPERTIES_PREFIX = "WITH"
     426
     427    # Whether to quote the generated expression of exp.JsonPath
     428    QUOTE_JSON_PATH = True
     429
     430    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
     431    PAD_FILL_PATTERN_IS_REQUIRED = False
     432
     433    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
     434    SUPPORTS_EXPLODING_PROJECTIONS = True
     435
     436    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
     437    ARRAY_CONCAT_IS_VAR_LEN = True
     438
     439    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
     440    SUPPORTS_CONVERT_TIMEZONE = False
     441
     442    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
     443    SUPPORTS_MEDIAN = True
     444
     445    # Whether UNIX_SECONDS(timestamp) is supported
     446    SUPPORTS_UNIX_SECONDS = False
     447
     448    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
     449    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
     450
     451    # The function name of the exp.ArraySize expression
     452    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
     453
     454    # The syntax to use when altering the type of a column
     455    ALTER_SET_TYPE = "SET DATA TYPE"
     456
     457    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
     458    # None -> Doesn't support it at all
     459    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
     460    # True (Postgres) -> Explicitly requires it
     461    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
     462
     463    TYPE_MAPPING = {
     464        exp.DataType.Type.DATETIME2: "TIMESTAMP",
     465        exp.DataType.Type.NCHAR: "CHAR",
     466        exp.DataType.Type.NVARCHAR: "VARCHAR",
     467        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     468        exp.DataType.Type.LONGTEXT: "TEXT",
     469        exp.DataType.Type.TINYTEXT: "TEXT",
     470        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     471        exp.DataType.Type.LONGBLOB: "BLOB",
     472        exp.DataType.Type.TINYBLOB: "BLOB",
     473        exp.DataType.Type.INET: "INET",
     474        exp.DataType.Type.ROWVERSION: "VARBINARY",
     475        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
     476    }
     477
     478    TIME_PART_SINGULARS = {
     479        "MICROSECONDS": "MICROSECOND",
     480        "SECONDS": "SECOND",
     481        "MINUTES": "MINUTE",
     482        "HOURS": "HOUR",
     483        "DAYS": "DAY",
     484        "WEEKS": "WEEK",
     485        "MONTHS": "MONTH",
     486        "QUARTERS": "QUARTER",
     487        "YEARS": "YEAR",
     488    }
     489
     490    AFTER_HAVING_MODIFIER_TRANSFORMS = {
     491        "cluster": lambda self, e: self.sql(e, "cluster"),
     492        "distribute": lambda self, e: self.sql(e, "distribute"),
     493        "sort": lambda self, e: self.sql(e, "sort"),
     494        "windows": lambda self, e: (
     495            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
     496            if e.args.get("windows")
     497            else ""
     498        ),
     499        "qualify": lambda self, e: self.sql(e, "qualify"),
     500    }
     501
     502    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     503
     504    STRUCT_DELIMITER = ("<", ">")
     505
     506    PARAMETER_TOKEN = "@"
     507    NAMED_PLACEHOLDER_TOKEN = ":"
     508
     509    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
     510
     511    PROPERTIES_LOCATION = {
     512        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
     513        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     514        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     515        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
     516        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
     517        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     518        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     519        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     520        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     521        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
     522        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     523        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
     524        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
     525        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
     526        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     527        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
     528        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     529        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     530        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     531        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
     532        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     533        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     534        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
     535        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
     536        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     537        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     538        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     539        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     540        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     541        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     542        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
     543        exp.HeapProperty: exp.Properties.Location.POST_WITH,
     544        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
     545        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
     546        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
     547        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
     548        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     549        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     550        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     551        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     552        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     553        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
     554        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     555        exp.LogProperty: exp.Properties.Location.POST_NAME,
     556        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     557        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     558        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     559        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
     560        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     561        exp.Order: exp.Properties.Location.POST_SCHEMA,
     562        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
     563        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     564        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
     565        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     566        exp.Property: exp.Properties.Location.POST_WITH,
     567        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
     568        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     569        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     570        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     571        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     572        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
     573        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     574        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
     575        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
     576        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     577        exp.Set: exp.Properties.Location.POST_SCHEMA,
     578        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     579        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     580        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
     581        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
     582        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
     583        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     584        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
     585        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     586        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     587        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
     588        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
     589        exp.Tags: exp.Properties.Location.POST_WITH,
     590        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     591        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
     592        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     593        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
     594        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     595        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
     596        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
     597        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     598        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     599        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     600        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
     601        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
     602        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
     603    }
     604
     605    # Keywords that can't be used as unquoted identifier names
     606    RESERVED_KEYWORDS: t.Set[str] = set()
     607
     608    # Expressions whose comments are separated from them for better formatting
     609    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     610        exp.Command,
     611        exp.Create,
     612        exp.Describe,
     613        exp.Delete,
     614        exp.Drop,
     615        exp.From,
     616        exp.Insert,
     617        exp.Join,
     618        exp.MultitableInserts,
     619        exp.Select,
     620        exp.SetOperation,
     621        exp.Update,
     622        exp.Where,
     623        exp.With,
     624    )
     625
     626    # Expressions that should not have their comments generated in maybe_comment
     627    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     628        exp.Binary,
     629        exp.SetOperation,
     630    )
     631
     632    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
     633    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
     634        exp.Column,
     635        exp.Literal,
     636        exp.Neg,
     637        exp.Paren,
     638    )
     639
     640    PARAMETERIZABLE_TEXT_TYPES = {
     641        exp.DataType.Type.NVARCHAR,
     642        exp.DataType.Type.VARCHAR,
     643        exp.DataType.Type.CHAR,
     644        exp.DataType.Type.NCHAR,
     645    }
     646
     647    # Expressions that need to have all CTEs under them bubbled up to them
     648    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
     649
     650    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     651
     652    __slots__ = (
     653        "pretty",
     654        "identify",
     655        "normalize",
     656        "pad",
     657        "_indent",
     658        "normalize_functions",
     659        "unsupported_level",
     660        "max_unsupported",
     661        "leading_comma",
     662        "max_text_width",
     663        "comments",
     664        "dialect",
     665        "unsupported_messages",
     666        "_escaped_quote_end",
     667        "_escaped_identifier_end",
     668        "_next_name",
     669        "_identifier_start",
     670        "_identifier_end",
     671        "_quote_json_path_key_using_brackets",
     672    )
     673
     674    def __init__(
     675        self,
     676        pretty: t.Optional[bool] = None,
     677        identify: str | bool = False,
     678        normalize: bool = False,
     679        pad: int = 2,
     680        indent: int = 2,
     681        normalize_functions: t.Optional[str | bool] = None,
     682        unsupported_level: ErrorLevel = ErrorLevel.WARN,
     683        max_unsupported: int = 3,
     684        leading_comma: bool = False,
     685        max_text_width: int = 80,
     686        comments: bool = True,
     687        dialect: DialectType = None,
     688    ):
     689        import sqlglot
     690        from sqlglot.dialects import Dialect
     691
     692        self.pretty = pretty if pretty is not None else sqlglot.pretty
     693        self.identify = identify
     694        self.normalize = normalize
     695        self.pad = pad
     696        self._indent = indent
     697        self.unsupported_level = unsupported_level
     698        self.max_unsupported = max_unsupported
     699        self.leading_comma = leading_comma
     700        self.max_text_width = max_text_width
     701        self.comments = comments
     702        self.dialect = Dialect.get_or_raise(dialect)
     703
     704        # This is both a Dialect property and a Generator argument, so we prioritize the latter
     705        self.normalize_functions = (
     706            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
     707        )
     708
     709        self.unsupported_messages: t.List[str] = []
     710        self._escaped_quote_end: str = (
     711            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
     712        )
     713        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
     714
     715        self._next_name = name_sequence("_t")
     716
     717        self._identifier_start = self.dialect.IDENTIFIER_START
     718        self._identifier_end = self.dialect.IDENTIFIER_END
     719
     720        self._quote_json_path_key_using_brackets = True
     721
     722    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
     723        """
     724        Generates the SQL string corresponding to the given syntax tree.
     725
     726        Args:
     727            expression: The syntax tree.
     728            copy: Whether to copy the expression. The generator performs mutations so
     729                it is safer to copy.
     730
     731        Returns:
     732            The SQL string corresponding to `expression`.
     733        """
     734        if copy:
     735            expression = expression.copy()
     736
     737        expression = self.preprocess(expression)
     738
     739        self.unsupported_messages = []
     740        sql = self.sql(expression).strip()
     741
     742        if self.pretty:
     743            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     744
     745        if self.unsupported_level == ErrorLevel.IGNORE:
     746            return sql
     747
     748        if self.unsupported_level == ErrorLevel.WARN:
     749            for msg in self.unsupported_messages:
     750                logger.warning(msg)
     751        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     752            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     753
     754        return sql
     755
     756    def preprocess(self, expression: exp.Expression) -> exp.Expression:
     757        """Apply generic preprocessing transformations to a given expression."""
     758        expression = self._move_ctes_to_top_level(expression)
     759
     760        if self.ENSURE_BOOLS:
     761            from sqlglot.transforms import ensure_bools
     762
     763            expression = ensure_bools(expression)
     764
     765        return expression
     766
     767    def _move_ctes_to_top_level(self, expression: E) -> E:
     768        if (
     769            not expression.parent
     770            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
     771            and any(node.parent is not expression for node in expression.find_all(exp.With))
     772        ):
     773            from sqlglot.transforms import move_ctes_to_top_level
     774
     775            expression = move_ctes_to_top_level(expression)
     776        return expression
     777
     778    def unsupported(self, message: str) -> None:
     779        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     780            raise UnsupportedError(message)
     781        self.unsupported_messages.append(message)
     782
     783    def sep(self, sep: str = " ") -> str:
     784        return f"{sep.strip()}\n" if self.pretty else sep
     785
     786    def seg(self, sql: str, sep: str = " ") -> str:
     787        return f"{self.sep(sep)}{sql}"
     788
     789    def pad_comment(self, comment: str) -> str:
     790        comment = " " + comment if comment[0].strip() else comment
     791        comment = comment + " " if comment[-1].strip() else comment
     792        return comment
     793
     794    def maybe_comment(
     795        self,
     796        sql: str,
     797        expression: t.Optional[exp.Expression] = None,
     798        comments: t.Optional[t.List[str]] = None,
     799        separated: bool = False,
     800    ) -> str:
     801        comments = (
     802            ((expression and expression.comments) if comments is None else comments)  # type: ignore
     803            if self.comments
     804            else None
     805        )
     806
     807        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
     808            return sql
     809
     810        comments_sql = " ".join(
     811            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     812        )
     813
     814        if not comments_sql:
     815            return sql
     816
     817        comments_sql = self._replace_line_breaks(comments_sql)
     818
     819        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     820            return (
     821                f"{self.sep()}{comments_sql}{sql}"
     822                if not sql or sql[0].isspace()
     823                else f"{comments_sql}{self.sep()}{sql}"
     824            )
     825
     826        return f"{sql} {comments_sql}"
     827
     828    def wrap(self, expression: exp.Expression | str) -> str:
     829        this_sql = (
     830            self.sql(expression)
     831            if isinstance(expression, exp.UNWRAPPED_QUERIES)
     832            else self.sql(expression, "this")
     833        )
     834        if not this_sql:
     835            return "()"
     836
     837        this_sql = self.indent(this_sql, level=1, pad=0)
     838        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     839
     840    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     841        original = self.identify
     842        self.identify = False
     843        result = func(*args, **kwargs)
     844        self.identify = original
     845        return result
     846
     847    def normalize_func(self, name: str) -> str:
     848        if self.normalize_functions == "upper" or self.normalize_functions is True:
     849            return name.upper()
     850        if self.normalize_functions == "lower":
     851            return name.lower()
     852        return name
     853
     854    def indent(
     855        self,
     856        sql: str,
     857        level: int = 0,
     858        pad: t.Optional[int] = None,
     859        skip_first: bool = False,
     860        skip_last: bool = False,
     861    ) -> str:
     862        if not self.pretty or not sql:
     863            return sql
     864
     865        pad = self.pad if pad is None else pad
     866        lines = sql.split("\n")
     867
     868        return "\n".join(
     869            (
     870                line
     871                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     872                else f"{' ' * (level * self._indent + pad)}{line}"
     873            )
     874            for i, line in enumerate(lines)
     875        )
     876
     877    def sql(
     878        self,
     879        expression: t.Optional[str | exp.Expression],
     880        key: t.Optional[str] = None,
     881        comment: bool = True,
     882    ) -> str:
     883        if not expression:
     884            return ""
     885
     886        if isinstance(expression, str):
     887            return expression
     888
     889        if key:
     890            value = expression.args.get(key)
     891            if value:
     892                return self.sql(value)
     893            return ""
     894
     895        transform = self.TRANSFORMS.get(expression.__class__)
     896
     897        if callable(transform):
     898            sql = transform(self, expression)
     899        elif isinstance(expression, exp.Expression):
     900            exp_handler_name = f"{expression.key}_sql"
     901
     902            if hasattr(self, exp_handler_name):
     903                sql = getattr(self, exp_handler_name)(expression)
     904            elif isinstance(expression, exp.Func):
     905                sql = self.function_fallback_sql(expression)
     906            elif isinstance(expression, exp.Property):
     907                sql = self.property_sql(expression)
     908            else:
     909                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     910        else:
     911            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     912
     913        return self.maybe_comment(sql, expression) if self.comments and comment else sql
     914
     915    def uncache_sql(self, expression: exp.Uncache) -> str:
     916        table = self.sql(expression, "this")
     917        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     918        return f"UNCACHE TABLE{exists_sql} {table}"
     919
     920    def cache_sql(self, expression: exp.Cache) -> str:
     921        lazy = " LAZY" if expression.args.get("lazy") else ""
     922        table = self.sql(expression, "this")
     923        options = expression.args.get("options")
     924        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     925        sql = self.sql(expression, "expression")
     926        sql = f" AS{self.sep()}{sql}" if sql else ""
     927        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     928        return self.prepend_ctes(expression, sql)
     929
     930    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     931        if isinstance(expression.parent, exp.Cast):
     932            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     933        default = "DEFAULT " if expression.args.get("default") else ""
     934        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     935
     936    def column_parts(self, expression: exp.Column) -> str:
     937        return ".".join(
     938            self.sql(part)
     939            for part in (
     940                expression.args.get("catalog"),
     941                expression.args.get("db"),
     942                expression.args.get("table"),
     943                expression.args.get("this"),
     944            )
     945            if part
     946        )
     947
     948    def column_sql(self, expression: exp.Column) -> str:
     949        join_mark = " (+)" if expression.args.get("join_mark") else ""
     950
     951        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
     952            join_mark = ""
     953            self.unsupported("Outer join syntax using the (+) operator is not supported.")
     954
     955        return f"{self.column_parts(expression)}{join_mark}"
     956
     957    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     958        this = self.sql(expression, "this")
     959        this = f" {this}" if this else ""
     960        position = self.sql(expression, "position")
     961        return f"{position}{this}"
     962
     963    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     964        column = self.sql(expression, "this")
     965        kind = self.sql(expression, "kind")
     966        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     967        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     968        kind = f"{sep}{kind}" if kind else ""
     969        constraints = f" {constraints}" if constraints else ""
     970        position = self.sql(expression, "position")
     971        position = f" {position}" if position else ""
     972
     973        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
     974            kind = ""
     975
     976        return f"{exists}{column}{kind}{constraints}{position}"
     977
     978    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     979        this = self.sql(expression, "this")
     980        kind_sql = self.sql(expression, "kind").strip()
     981        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     982
     983    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     984        this = self.sql(expression, "this")
     985        if expression.args.get("not_null"):
     986            persisted = " PERSISTED NOT NULL"
     987        elif expression.args.get("persisted"):
     988            persisted = " PERSISTED"
     989        else:
     990            persisted = ""
     991        return f"AS {this}{persisted}"
     992
     993    def autoincrementcolumnconstraint_sql(self, _) -> str:
     994        return self.token_sql(TokenType.AUTO_INCREMENT)
     995
     996    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     997        if isinstance(expression.this, list):
     998            this = self.wrap(self.expressions(expression, key="this", flat=True))
     999        else:
    1000            this = self.sql(expression, "this")
    1001
    1002        return f"COMPRESS {this}"
    1003
    1004    def generatedasidentitycolumnconstraint_sql(
    1005        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1006    ) -> str:
    1007        this = ""
    1008        if expression.this is not None:
    1009            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1010            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1011
    1012        start = expression.args.get("start")
    1013        start = f"START WITH {start}" if start else ""
    1014        increment = expression.args.get("increment")
    1015        increment = f" INCREMENT BY {increment}" if increment else ""
    1016        minvalue = expression.args.get("minvalue")
    1017        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1018        maxvalue = expression.args.get("maxvalue")
    1019        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1020        cycle = expression.args.get("cycle")
    1021        cycle_sql = ""
    1022
    1023        if cycle is not None:
    1024            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1025            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1026
    1027        sequence_opts = ""
    1028        if start or increment or cycle_sql:
    1029            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1030            sequence_opts = f" ({sequence_opts.strip()})"
    1031
    1032        expr = self.sql(expression, "expression")
    1033        expr = f"({expr})" if expr else "IDENTITY"
    1034
    1035        return f"GENERATED{this} AS {expr}{sequence_opts}"
    1036
    1037    def generatedasrowcolumnconstraint_sql(
    1038        self, expression: exp.GeneratedAsRowColumnConstraint
    1039    ) -> str:
    1040        start = "START" if expression.args.get("start") else "END"
    1041        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1042        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
    1043
    1044    def periodforsystemtimeconstraint_sql(
    1045        self, expression: exp.PeriodForSystemTimeConstraint
    1046    ) -> str:
    1047        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
    1048
    1049    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
    1050        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
    1051
    1052    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
    1053        return f"AS {self.sql(expression, 'this')}"
    1054
    1055    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1056        desc = expression.args.get("desc")
    1057        if desc is not None:
    1058            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1059        return "PRIMARY KEY"
    1060
    1061    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1062        this = self.sql(expression, "this")
    1063        this = f" {this}" if this else ""
    1064        index_type = expression.args.get("index_type")
    1065        index_type = f" USING {index_type}" if index_type else ""
    1066        on_conflict = self.sql(expression, "on_conflict")
    1067        on_conflict = f" {on_conflict}" if on_conflict else ""
    1068        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1069        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}"
    1070
    1071    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
    1072        return self.sql(expression, "this")
    1073
    1074    def create_sql(self, expression: exp.Create) -> str:
    1075        kind = self.sql(expression, "kind")
    1076        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1077        properties = expression.args.get("properties")
    1078        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1079
    1080        this = self.createable_sql(expression, properties_locs)
    1081
    1082        properties_sql = ""
    1083        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1084            exp.Properties.Location.POST_WITH
    1085        ):
    1086            properties_sql = self.sql(
    1087                exp.Properties(
    1088                    expressions=[
    1089                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1090                        *properties_locs[exp.Properties.Location.POST_WITH],
    1091                    ]
    1092                )
    1093            )
    1094
    1095            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1096                properties_sql = self.sep() + properties_sql
    1097            elif not self.pretty:
    1098                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1099                properties_sql = f" {properties_sql}"
    1100
    1101        begin = " BEGIN" if expression.args.get("begin") else ""
    1102        end = " END" if expression.args.get("end") else ""
    1103
    1104        expression_sql = self.sql(expression, "expression")
    1105        if expression_sql:
    1106            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1107
    1108            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1109                postalias_props_sql = ""
    1110                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1111                    postalias_props_sql = self.properties(
    1112                        exp.Properties(
    1113                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1114                        ),
    1115                        wrapped=False,
    1116                    )
    1117                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1118                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1119
    1120        postindex_props_sql = ""
    1121        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1122            postindex_props_sql = self.properties(
    1123                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1124                wrapped=False,
    1125                prefix=" ",
    1126            )
    1127
    1128        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1129        indexes = f" {indexes}" if indexes else ""
    1130        index_sql = indexes + postindex_props_sql
    1131
    1132        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1133        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1134        unique = " UNIQUE" if expression.args.get("unique") else ""
    1135
    1136        clustered = expression.args.get("clustered")
    1137        if clustered is None:
    1138            clustered_sql = ""
    1139        elif clustered:
    1140            clustered_sql = " CLUSTERED COLUMNSTORE"
    1141        else:
    1142            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1143
    1144        postcreate_props_sql = ""
    1145        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1146            postcreate_props_sql = self.properties(
    1147                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1148                sep=" ",
    1149                prefix=" ",
    1150                wrapped=False,
    1151            )
    1152
    1153        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1154
    1155        postexpression_props_sql = ""
    1156        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1157            postexpression_props_sql = self.properties(
    1158                exp.Properties(
    1159                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1160                ),
    1161                sep=" ",
    1162                prefix=" ",
    1163                wrapped=False,
    1164            )
    1165
    1166        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1167        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1168        no_schema_binding = (
    1169            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1170        )
    1171
    1172        clone = self.sql(expression, "clone")
    1173        clone = f" {clone}" if clone else ""
    1174
    1175        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1176            properties_expression = f"{expression_sql}{properties_sql}"
    1177        else:
    1178            properties_expression = f"{properties_sql}{expression_sql}"
    1179
    1180        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1181        return self.prepend_ctes(expression, expression_sql)
    1182
    1183    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1184        start = self.sql(expression, "start")
    1185        start = f"START WITH {start}" if start else ""
    1186        increment = self.sql(expression, "increment")
    1187        increment = f" INCREMENT BY {increment}" if increment else ""
    1188        minvalue = self.sql(expression, "minvalue")
    1189        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1190        maxvalue = self.sql(expression, "maxvalue")
    1191        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1192        owned = self.sql(expression, "owned")
    1193        owned = f" OWNED BY {owned}" if owned else ""
    1194
    1195        cache = expression.args.get("cache")
    1196        if cache is None:
    1197            cache_str = ""
    1198        elif cache is True:
    1199            cache_str = " CACHE"
    1200        else:
    1201            cache_str = f" CACHE {cache}"
    1202
    1203        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1204        options = f" {options}" if options else ""
    1205
    1206        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
    1207
    1208    def clone_sql(self, expression: exp.Clone) -> str:
    1209        this = self.sql(expression, "this")
    1210        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1211        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1212        return f"{shallow}{keyword} {this}"
    1213
    1214    def describe_sql(self, expression: exp.Describe) -> str:
    1215        style = expression.args.get("style")
    1216        style = f" {style}" if style else ""
    1217        partition = self.sql(expression, "partition")
    1218        partition = f" {partition}" if partition else ""
    1219        format = self.sql(expression, "format")
    1220        format = f" {format}" if format else ""
    1221
    1222        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
    1223
    1224    def heredoc_sql(self, expression: exp.Heredoc) -> str:
    1225        tag = self.sql(expression, "tag")
    1226        return f"${tag}${self.sql(expression, 'this')}${tag}$"
    1227
    1228    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1229        with_ = self.sql(expression, "with")
    1230        if with_:
    1231            sql = f"{with_}{self.sep()}{sql}"
    1232        return sql
    1233
    1234    def with_sql(self, expression: exp.With) -> str:
    1235        sql = self.expressions(expression, flat=True)
    1236        recursive = (
    1237            "RECURSIVE "
    1238            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1239            else ""
    1240        )
    1241
    1242        return f"WITH {recursive}{sql}"
    1243
    1244    def cte_sql(self, expression: exp.CTE) -> str:
    1245        alias = expression.args.get("alias")
    1246        if alias:
    1247            alias.add_comments(expression.pop_comments())
    1248
    1249        alias_sql = self.sql(expression, "alias")
    1250
    1251        materialized = expression.args.get("materialized")
    1252        if materialized is False:
    1253            materialized = "NOT MATERIALIZED "
    1254        elif materialized:
    1255            materialized = "MATERIALIZED "
    1256
    1257        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
    1258
    1259    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1260        alias = self.sql(expression, "this")
    1261        columns = self.expressions(expression, key="columns", flat=True)
    1262        columns = f"({columns})" if columns else ""
    1263
    1264        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1265            columns = ""
    1266            self.unsupported("Named columns are not supported in table alias.")
    1267
    1268        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1269            alias = self._next_name()
    1270
    1271        return f"{alias}{columns}"
    1272
    1273    def bitstring_sql(self, expression: exp.BitString) -> str:
    1274        this = self.sql(expression, "this")
    1275        if self.dialect.BIT_START:
    1276            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1277        return f"{int(this, 2)}"
    1278
    1279    def hexstring_sql(self, expression: exp.HexString) -> str:
    1280        this = self.sql(expression, "this")
    1281        if self.dialect.HEX_START:
    1282            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1283        return f"{int(this, 16)}"
    1284
    1285    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1286        this = self.sql(expression, "this")
    1287        if self.dialect.BYTE_START:
    1288            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1289        return this
    1290
    1291    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1292        this = self.sql(expression, "this")
    1293        escape = expression.args.get("escape")
    1294
    1295        if self.dialect.UNICODE_START:
    1296            escape_substitute = r"\\\1"
    1297            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1298        else:
    1299            escape_substitute = r"\\u\1"
    1300            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1301
    1302        if escape:
    1303            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1304            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1305        else:
    1306            escape_pattern = ESCAPED_UNICODE_RE
    1307            escape_sql = ""
    1308
    1309        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1310            this = escape_pattern.sub(escape_substitute, this)
    1311
    1312        return f"{left_quote}{this}{right_quote}{escape_sql}"
    1313
    1314    def rawstring_sql(self, expression: exp.RawString) -> str:
    1315        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
    1316        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
    1317
    1318    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1319        this = self.sql(expression, "this")
    1320        specifier = self.sql(expression, "expression")
    1321        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1322        return f"{this}{specifier}"
    1323
    1324    def datatype_sql(self, expression: exp.DataType) -> str:
    1325        nested = ""
    1326        values = ""
    1327        interior = self.expressions(expression, flat=True)
    1328
    1329        type_value = expression.this
    1330        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1331            type_sql = self.sql(expression, "kind")
    1332        else:
    1333            type_sql = (
    1334                self.TYPE_MAPPING.get(type_value, type_value.value)
    1335                if isinstance(type_value, exp.DataType.Type)
    1336                else type_value
    1337            )
    1338
    1339        if interior:
    1340            if expression.args.get("nested"):
    1341                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1342                if expression.args.get("values") is not None:
    1343                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1344                    values = self.expressions(expression, key="values", flat=True)
    1345                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1346            elif type_value == exp.DataType.Type.INTERVAL:
    1347                nested = f" {interior}"
    1348            else:
    1349                nested = f"({interior})"
    1350
    1351        type_sql = f"{type_sql}{nested}{values}"
    1352        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1353            exp.DataType.Type.TIMETZ,
    1354            exp.DataType.Type.TIMESTAMPTZ,
    1355        ):
    1356            type_sql = f"{type_sql} WITH TIME ZONE"
    1357
    1358        return type_sql
    1359
    1360    def directory_sql(self, expression: exp.Directory) -> str:
    1361        local = "LOCAL " if expression.args.get("local") else ""
    1362        row_format = self.sql(expression, "row_format")
    1363        row_format = f" {row_format}" if row_format else ""
    1364        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
    1365
    1366    def delete_sql(self, expression: exp.Delete) -> str:
    1367        this = self.sql(expression, "this")
    1368        this = f" FROM {this}" if this else ""
    1369        using = self.sql(expression, "using")
    1370        using = f" USING {using}" if using else ""
    1371        cluster = self.sql(expression, "cluster")
    1372        cluster = f" {cluster}" if cluster else ""
    1373        where = self.sql(expression, "where")
    1374        returning = self.sql(expression, "returning")
    1375        limit = self.sql(expression, "limit")
    1376        tables = self.expressions(expression, key="tables")
    1377        tables = f" {tables}" if tables else ""
    1378        if self.RETURNING_END:
    1379            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1380        else:
    1381            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1382        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
    1383
    1384    def drop_sql(self, expression: exp.Drop) -> str:
    1385        this = self.sql(expression, "this")
    1386        expressions = self.expressions(expression, flat=True)
    1387        expressions = f" ({expressions})" if expressions else ""
    1388        kind = expression.args["kind"]
    1389        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1390        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1391        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1392        on_cluster = self.sql(expression, "cluster")
    1393        on_cluster = f" {on_cluster}" if on_cluster else ""
    1394        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1395        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1396        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1397        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1398        purge = " PURGE" if expression.args.get("purge") else ""
    1399        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
    1400
    1401    def set_operation(self, expression: exp.SetOperation) -> str:
    1402        op_type = type(expression)
    1403        op_name = op_type.key.upper()
    1404
    1405        distinct = expression.args.get("distinct")
    1406        if (
    1407            distinct is False
    1408            and op_type in (exp.Except, exp.Intersect)
    1409            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1410        ):
    1411            self.unsupported(f"{op_name} ALL is not supported")
    1412
    1413        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1414
    1415        if distinct is None:
    1416            distinct = default_distinct
    1417            if distinct is None:
    1418                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1419
    1420        if distinct is default_distinct:
    1421            kind = ""
    1422        else:
    1423            kind = " DISTINCT" if distinct else " ALL"
    1424
    1425        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1426        return f"{op_name}{kind}{by_name}"
    1427
    1428    def set_operations(self, expression: exp.SetOperation) -> str:
    1429        if not self.SET_OP_MODIFIERS:
    1430            limit = expression.args.get("limit")
    1431            order = expression.args.get("order")
    1432
    1433            if limit or order:
    1434                select = self._move_ctes_to_top_level(
    1435                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1436                )
    1437
    1438                if limit:
    1439                    select = select.limit(limit.pop(), copy=False)
    1440                if order:
    1441                    select = select.order_by(order.pop(), copy=False)
    1442                return self.sql(select)
    1443
    1444        sqls: t.List[str] = []
    1445        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1446
    1447        while stack:
    1448            node = stack.pop()
    1449
    1450            if isinstance(node, exp.SetOperation):
    1451                stack.append(node.expression)
    1452                stack.append(
    1453                    self.maybe_comment(
    1454                        self.set_operation(node), comments=node.comments, separated=True
    1455                    )
    1456                )
    1457                stack.append(node.this)
    1458            else:
    1459                sqls.append(self.sql(node))
    1460
    1461        this = self.sep().join(sqls)
    1462        this = self.query_modifiers(expression, this)
    1463        return self.prepend_ctes(expression, this)
    1464
    1465    def fetch_sql(self, expression: exp.Fetch) -> str:
    1466        direction = expression.args.get("direction")
    1467        direction = f" {direction}" if direction else ""
    1468        count = self.sql(expression, "count")
    1469        count = f" {count}" if count else ""
    1470        if expression.args.get("percent"):
    1471            count = f"{count} PERCENT"
    1472        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1473        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
    1474
    1475    def filter_sql(self, expression: exp.Filter) -> str:
    1476        if self.AGGREGATE_FILTER_SUPPORTED:
    1477            this = self.sql(expression, "this")
    1478            where = self.sql(expression, "expression").strip()
    1479            return f"{this} FILTER({where})"
    1480
    1481        agg = expression.this
    1482        agg_arg = agg.this
    1483        cond = expression.expression.this
    1484        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1485        return self.sql(agg)
    1486
    1487    def hint_sql(self, expression: exp.Hint) -> str:
    1488        if not self.QUERY_HINTS:
    1489            self.unsupported("Hints are not supported")
    1490            return ""
    1491
    1492        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
    1493
    1494    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1495        using = self.sql(expression, "using")
    1496        using = f" USING {using}" if using else ""
    1497        columns = self.expressions(expression, key="columns", flat=True)
    1498        columns = f"({columns})" if columns else ""
    1499        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1500        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1501        where = self.sql(expression, "where")
    1502        include = self.expressions(expression, key="include", flat=True)
    1503        if include:
    1504            include = f" INCLUDE ({include})"
    1505        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1506        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1507        tablespace = self.sql(expression, "tablespace")
    1508        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1509        on = self.sql(expression, "on")
    1510        on = f" ON {on}" if on else ""
    1511
    1512        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
    1513
    1514    def index_sql(self, expression: exp.Index) -> str:
    1515        unique = "UNIQUE " if expression.args.get("unique") else ""
    1516        primary = "PRIMARY " if expression.args.get("primary") else ""
    1517        amp = "AMP " if expression.args.get("amp") else ""
    1518        name = self.sql(expression, "this")
    1519        name = f"{name} " if name else ""
    1520        table = self.sql(expression, "table")
    1521        table = f"{self.INDEX_ON} {table}" if table else ""
    1522
    1523        index = "INDEX " if not table else ""
    1524
    1525        params = self.sql(expression, "params")
    1526        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
    1527
    1528    def identifier_sql(self, expression: exp.Identifier) -> str:
    1529        text = expression.name
    1530        lower = text.lower()
    1531        text = lower if self.normalize and not expression.quoted else text
    1532        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1533        if (
    1534            expression.quoted
    1535            or self.dialect.can_identify(text, self.identify)
    1536            or lower in self.RESERVED_KEYWORDS
    1537            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1538        ):
    1539            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1540        return text
    1541
    1542    def hex_sql(self, expression: exp.Hex) -> str:
    1543        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1544        if self.dialect.HEX_LOWERCASE:
    1545            text = self.func("LOWER", text)
    1546
    1547        return text
    1548
    1549    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1550        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1551        if not self.dialect.HEX_LOWERCASE:
    1552            text = self.func("LOWER", text)
    1553        return text
    1554
    1555    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1556        input_format = self.sql(expression, "input_format")
    1557        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1558        output_format = self.sql(expression, "output_format")
    1559        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1560        return self.sep().join((input_format, output_format))
    1561
    1562    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1563        string = self.sql(exp.Literal.string(expression.name))
    1564        return f"{prefix}{string}"
    1565
    1566    def partition_sql(self, expression: exp.Partition) -> str:
    1567        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
    1568        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
    1569
    1570    def properties_sql(self, expression: exp.Properties) -> str:
    1571        root_properties = []
    1572        with_properties = []
    1573
    1574        for p in expression.expressions:
    1575            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1576            if p_loc == exp.Properties.Location.POST_WITH:
    1577                with_properties.append(p)
    1578            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1579                root_properties.append(p)
    1580
    1581        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1582        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1583
    1584        if root_props and with_props and not self.pretty:
    1585            with_props = " " + with_props
    1586
    1587        return root_props + with_props
    1588
    1589    def root_properties(self, properties: exp.Properties) -> str:
    1590        if properties.expressions:
    1591            return self.expressions(properties, indent=False, sep=" ")
    1592        return ""
    1593
    1594    def properties(
    1595        self,
    1596        properties: exp.Properties,
    1597        prefix: str = "",
    1598        sep: str = ", ",
    1599        suffix: str = "",
    1600        wrapped: bool = True,
    1601    ) -> str:
    1602        if properties.expressions:
    1603            expressions = self.expressions(properties, sep=sep, indent=False)
    1604            if expressions:
    1605                expressions = self.wrap(expressions) if wrapped else expressions
    1606                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1607        return ""
    1608
    1609    def with_properties(self, properties: exp.Properties) -> str:
    1610        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
    1611
    1612    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1613        properties_locs = defaultdict(list)
    1614        for p in properties.expressions:
    1615            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1616            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1617                properties_locs[p_loc].append(p)
    1618            else:
    1619                self.unsupported(f"Unsupported property {p.key}")
    1620
    1621        return properties_locs
    1622
    1623    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1624        if isinstance(expression.this, exp.Dot):
    1625            return self.sql(expression, "this")
    1626        return f"'{expression.name}'" if string_key else expression.name
    1627
    1628    def property_sql(self, expression: exp.Property) -> str:
    1629        property_cls = expression.__class__
    1630        if property_cls == exp.Property:
    1631            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1632
    1633        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1634        if not property_name:
    1635            self.unsupported(f"Unsupported property {expression.key}")
    1636
    1637        return f"{property_name}={self.sql(expression, 'this')}"
    1638
    1639    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1640        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1641            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1642            options = f" {options}" if options else ""
    1643
    1644            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1645            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1646                like = f"({like})"
    1647
    1648            return like
    1649
    1650        if expression.expressions:
    1651            self.unsupported("Transpilation of LIKE property options is unsupported")
    1652
    1653        select = exp.select("*").from_(expression.this).limit(0)
    1654        return f"AS {self.sql(select)}"
    1655
    1656    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1657        no = "NO " if expression.args.get("no") else ""
    1658        protection = " PROTECTION" if expression.args.get("protection") else ""
    1659        return f"{no}FALLBACK{protection}"
    1660
    1661    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1662        no = "NO " if expression.args.get("no") else ""
    1663        local = expression.args.get("local")
    1664        local = f"{local} " if local else ""
    1665        dual = "DUAL " if expression.args.get("dual") else ""
    1666        before = "BEFORE " if expression.args.get("before") else ""
    1667        after = "AFTER " if expression.args.get("after") else ""
    1668        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1669
    1670    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1671        freespace = self.sql(expression, "this")
    1672        percent = " PERCENT" if expression.args.get("percent") else ""
    1673        return f"FREESPACE={freespace}{percent}"
    1674
    1675    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1676        if expression.args.get("default"):
    1677            property = "DEFAULT"
    1678        elif expression.args.get("on"):
    1679            property = "ON"
    1680        else:
    1681            property = "OFF"
    1682        return f"CHECKSUM={property}"
    1683
    1684    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1685        if expression.args.get("no"):
    1686            return "NO MERGEBLOCKRATIO"
    1687        if expression.args.get("default"):
    1688            return "DEFAULT MERGEBLOCKRATIO"
    1689
    1690        percent = " PERCENT" if expression.args.get("percent") else ""
    1691        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1692
    1693    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1694        default = expression.args.get("default")
    1695        minimum = expression.args.get("minimum")
    1696        maximum = expression.args.get("maximum")
    1697        if default or minimum or maximum:
    1698            if default:
    1699                prop = "DEFAULT"
    1700            elif minimum:
    1701                prop = "MINIMUM"
    1702            else:
    1703                prop = "MAXIMUM"
    1704            return f"{prop} DATABLOCKSIZE"
    1705        units = expression.args.get("units")
    1706        units = f" {units}" if units else ""
    1707        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1708
    1709    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1710        autotemp = expression.args.get("autotemp")
    1711        always = expression.args.get("always")
    1712        default = expression.args.get("default")
    1713        manual = expression.args.get("manual")
    1714        never = expression.args.get("never")
    1715
    1716        if autotemp is not None:
    1717            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1718        elif always:
    1719            prop = "ALWAYS"
    1720        elif default:
    1721            prop = "DEFAULT"
    1722        elif manual:
    1723            prop = "MANUAL"
    1724        elif never:
    1725            prop = "NEVER"
    1726        return f"BLOCKCOMPRESSION={prop}"
    1727
    1728    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1729        no = expression.args.get("no")
    1730        no = " NO" if no else ""
    1731        concurrent = expression.args.get("concurrent")
    1732        concurrent = " CONCURRENT" if concurrent else ""
    1733        target = self.sql(expression, "target")
    1734        target = f" {target}" if target else ""
    1735        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
    1736
    1737    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1738        if isinstance(expression.this, list):
    1739            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1740        if expression.this:
    1741            modulus = self.sql(expression, "this")
    1742            remainder = self.sql(expression, "expression")
    1743            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1744
    1745        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1746        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1747        return f"FROM ({from_expressions}) TO ({to_expressions})"
    1748
    1749    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1750        this = self.sql(expression, "this")
    1751
    1752        for_values_or_default = expression.expression
    1753        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1754            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1755        else:
    1756            for_values_or_default = " DEFAULT"
    1757
    1758        return f"PARTITION OF {this}{for_values_or_default}"
    1759
    1760    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1761        kind = expression.args.get("kind")
    1762        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1763        for_or_in = expression.args.get("for_or_in")
    1764        for_or_in = f" {for_or_in}" if for_or_in else ""
    1765        lock_type = expression.args.get("lock_type")
    1766        override = " OVERRIDE" if expression.args.get("override") else ""
    1767        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
    1768
    1769    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1770        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1771        statistics = expression.args.get("statistics")
    1772        statistics_sql = ""
    1773        if statistics is not None:
    1774            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1775        return f"{data_sql}{statistics_sql}"
    1776
    1777    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1778        this = self.sql(expression, "this")
    1779        this = f"HISTORY_TABLE={this}" if this else ""
    1780        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1781        data_consistency = (
    1782            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1783        )
    1784        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1785        retention_period = (
    1786            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1787        )
    1788
    1789        if this:
    1790            on_sql = self.func("ON", this, data_consistency, retention_period)
    1791        else:
    1792            on_sql = "ON" if expression.args.get("on") else "OFF"
    1793
    1794        sql = f"SYSTEM_VERSIONING={on_sql}"
    1795
    1796        return f"WITH({sql})" if expression.args.get("with") else sql
    1797
    1798    def insert_sql(self, expression: exp.Insert) -> str:
    1799        hint = self.sql(expression, "hint")
    1800        overwrite = expression.args.get("overwrite")
    1801
    1802        if isinstance(expression.this, exp.Directory):
    1803            this = " OVERWRITE" if overwrite else " INTO"
    1804        else:
    1805            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1806
    1807        stored = self.sql(expression, "stored")
    1808        stored = f" {stored}" if stored else ""
    1809        alternative = expression.args.get("alternative")
    1810        alternative = f" OR {alternative}" if alternative else ""
    1811        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1812        is_function = expression.args.get("is_function")
    1813        if is_function:
    1814            this = f"{this} FUNCTION"
    1815        this = f"{this} {self.sql(expression, 'this')}"
    1816
    1817        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1818        where = self.sql(expression, "where")
    1819        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1820        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1821        on_conflict = self.sql(expression, "conflict")
    1822        on_conflict = f" {on_conflict}" if on_conflict else ""
    1823        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1824        returning = self.sql(expression, "returning")
    1825
    1826        if self.RETURNING_END:
    1827            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1828        else:
    1829            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1830
    1831        partition_by = self.sql(expression, "partition")
    1832        partition_by = f" {partition_by}" if partition_by else ""
    1833        settings = self.sql(expression, "settings")
    1834        settings = f" {settings}" if settings else ""
    1835
    1836        source = self.sql(expression, "source")
    1837        source = f"TABLE {source}" if source else ""
    1838
    1839        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1840        return self.prepend_ctes(expression, sql)
    1841
    1842    def introducer_sql(self, expression: exp.Introducer) -> str:
    1843        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1844
    1845    def kill_sql(self, expression: exp.Kill) -> str:
    1846        kind = self.sql(expression, "kind")
    1847        kind = f" {kind}" if kind else ""
    1848        this = self.sql(expression, "this")
    1849        this = f" {this}" if this else ""
    1850        return f"KILL{kind}{this}"
    1851
    1852    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1853        return expression.name
    1854
    1855    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1856        return expression.name
    1857
    1858    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1859        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1860
    1861        constraint = self.sql(expression, "constraint")
    1862        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1863
    1864        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1865        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1866        action = self.sql(expression, "action")
    1867
    1868        expressions = self.expressions(expression, flat=True)
    1869        if expressions:
    1870            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1871            expressions = f" {set_keyword}{expressions}"
    1872
    1873        where = self.sql(expression, "where")
    1874        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
    1875
    1876    def returning_sql(self, expression: exp.Returning) -> str:
    1877        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1878
    1879    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1880        fields = self.sql(expression, "fields")
    1881        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1882        escaped = self.sql(expression, "escaped")
    1883        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1884        items = self.sql(expression, "collection_items")
    1885        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1886        keys = self.sql(expression, "map_keys")
    1887        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1888        lines = self.sql(expression, "lines")
    1889        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1890        null = self.sql(expression, "null")
    1891        null = f" NULL DEFINED AS {null}" if null else ""
    1892        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1893
    1894    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1895        return f"WITH ({self.expressions(expression, flat=True)})"
    1896
    1897    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1898        this = f"{self.sql(expression, 'this')} INDEX"
    1899        target = self.sql(expression, "target")
    1900        target = f" FOR {target}" if target else ""
    1901        return f"{this}{target} ({self.expressions(expression, flat=True)})"
    1902
    1903    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1904        this = self.sql(expression, "this")
    1905        kind = self.sql(expression, "kind")
    1906        expr = self.sql(expression, "expression")
    1907        return f"{this} ({kind} => {expr})"
    1908
    1909    def table_parts(self, expression: exp.Table) -> str:
    1910        return ".".join(
    1911            self.sql(part)
    1912            for part in (
    1913                expression.args.get("catalog"),
    1914                expression.args.get("db"),
    1915                expression.args.get("this"),
    1916            )
    1917            if part is not None
    1918        )
    1919
    1920    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1921        table = self.table_parts(expression)
    1922        only = "ONLY " if expression.args.get("only") else ""
    1923        partition = self.sql(expression, "partition")
    1924        partition = f" {partition}" if partition else ""
    1925        version = self.sql(expression, "version")
    1926        version = f" {version}" if version else ""
    1927        alias = self.sql(expression, "alias")
    1928        alias = f"{sep}{alias}" if alias else ""
    1929
    1930        sample = self.sql(expression, "sample")
    1931        if self.dialect.ALIAS_POST_TABLESAMPLE:
    1932            sample_pre_alias = sample
    1933            sample_post_alias = ""
    1934        else:
    1935            sample_pre_alias = ""
    1936            sample_post_alias = sample
    1937
    1938        hints = self.expressions(expression, key="hints", sep=" ")
    1939        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1940        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    1941        joins = self.indent(
    1942            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1943        )
    1944        laterals = self.expressions(expression, key="laterals", sep="")
    1945
    1946        file_format = self.sql(expression, "format")
    1947        if file_format:
    1948            pattern = self.sql(expression, "pattern")
    1949            pattern = f", PATTERN => {pattern}" if pattern else ""
    1950            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1951
    1952        ordinality = expression.args.get("ordinality") or ""
    1953        if ordinality:
    1954            ordinality = f" WITH ORDINALITY{alias}"
    1955            alias = ""
    1956
    1957        when = self.sql(expression, "when")
    1958        if when:
    1959            table = f"{table} {when}"
    1960
    1961        changes = self.sql(expression, "changes")
    1962        changes = f" {changes}" if changes else ""
    1963
    1964        rows_from = self.expressions(expression, key="rows_from")
    1965        if rows_from:
    1966            table = f"ROWS FROM {self.wrap(rows_from)}"
    1967
    1968        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
    1969
    1970    def tablesample_sql(
    1971        self,
    1972        expression: exp.TableSample,
    1973        tablesample_keyword: t.Optional[str] = None,
    1974    ) -> str:
    1975        method = self.sql(expression, "method")
    1976        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1977        numerator = self.sql(expression, "bucket_numerator")
    1978        denominator = self.sql(expression, "bucket_denominator")
    1979        field = self.sql(expression, "bucket_field")
    1980        field = f" ON {field}" if field else ""
    1981        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1982        seed = self.sql(expression, "seed")
    1983        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1984
    1985        size = self.sql(expression, "size")
    1986        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1987            size = f"{size} ROWS"
    1988
    1989        percent = self.sql(expression, "percent")
    1990        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1991            percent = f"{percent} PERCENT"
    1992
    1993        expr = f"{bucket}{percent}{size}"
    1994        if self.TABLESAMPLE_REQUIRES_PARENS:
    1995            expr = f"({expr})"
    1996
    1997        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
    1998
    1999    def pivot_sql(self, expression: exp.Pivot) -> str:
    2000        expressions = self.expressions(expression, flat=True)
    2001        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2002
    2003        if expression.this:
    2004            this = self.sql(expression, "this")
    2005            if not expressions:
    2006                return f"UNPIVOT {this}"
    2007
    2008            on = f"{self.seg('ON')} {expressions}"
    2009            into = self.sql(expression, "into")
    2010            into = f"{self.seg('INTO')} {into}" if into else ""
    2011            using = self.expressions(expression, key="using", flat=True)
    2012            using = f"{self.seg('USING')} {using}" if using else ""
    2013            group = self.sql(expression, "group")
    2014            return f"{direction} {this}{on}{into}{using}{group}"
    2015
    2016        alias = self.sql(expression, "alias")
    2017        alias = f" AS {alias}" if alias else ""
    2018
    2019        field = self.sql(expression, "field")
    2020
    2021        include_nulls = expression.args.get("include_nulls")
    2022        if include_nulls is not None:
    2023            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2024        else:
    2025            nulls = ""
    2026
    2027        default_on_null = self.sql(expression, "default_on_null")
    2028        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2029        return f"{self.seg(direction)}{nulls}({expressions} FOR {field}{default_on_null}){alias}"
    2030
    2031    def version_sql(self, expression: exp.Version) -> str:
    2032        this = f"FOR {expression.name}"
    2033        kind = expression.text("kind")
    2034        expr = self.sql(expression, "expression")
    2035        return f"{this} {kind} {expr}"
    2036
    2037    def tuple_sql(self, expression: exp.Tuple) -> str:
    2038        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    2039
    2040    def update_sql(self, expression: exp.Update) -> str:
    2041        this = self.sql(expression, "this")
    2042        set_sql = self.expressions(expression, flat=True)
    2043        from_sql = self.sql(expression, "from")
    2044        where_sql = self.sql(expression, "where")
    2045        returning = self.sql(expression, "returning")
    2046        order = self.sql(expression, "order")
    2047        limit = self.sql(expression, "limit")
    2048        if self.RETURNING_END:
    2049            expression_sql = f"{from_sql}{where_sql}{returning}"
    2050        else:
    2051            expression_sql = f"{returning}{from_sql}{where_sql}"
    2052        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2053        return self.prepend_ctes(expression, sql)
    2054
    2055    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2056        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2057
    2058        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2059        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2060            args = self.expressions(expression)
    2061            alias = self.sql(expression, "alias")
    2062            values = f"VALUES{self.seg('')}{args}"
    2063            values = (
    2064                f"({values})"
    2065                if self.WRAP_DERIVED_VALUES
    2066                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2067                else values
    2068            )
    2069            return f"{values} AS {alias}" if alias else values
    2070
    2071        # Converts `VALUES...` expression into a series of select unions.
    2072        alias_node = expression.args.get("alias")
    2073        column_names = alias_node and alias_node.columns
    2074
    2075        selects: t.List[exp.Query] = []
    2076
    2077        for i, tup in enumerate(expression.expressions):
    2078            row = tup.expressions
    2079
    2080            if i == 0 and column_names:
    2081                row = [
    2082                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2083                ]
    2084
    2085            selects.append(exp.Select(expressions=row))
    2086
    2087        if self.pretty:
    2088            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2089            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2090            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2091            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2092            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2093
    2094        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2095        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2096        return f"({unions}){alias}"
    2097
    2098    def var_sql(self, expression: exp.Var) -> str:
    2099        return self.sql(expression, "this")
    2100
    2101    @unsupported_args("expressions")
    2102    def into_sql(self, expression: exp.Into) -> str:
    2103        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2104        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2105        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    2106
    2107    def from_sql(self, expression: exp.From) -> str:
    2108        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    2109
    2110    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
    2111        grouping_sets = self.expressions(expression, indent=False)
    2112        return f"GROUPING SETS {self.wrap(grouping_sets)}"
    2113
    2114    def rollup_sql(self, expression: exp.Rollup) -> str:
    2115        expressions = self.expressions(expression, indent=False)
    2116        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
    2117
    2118    def cube_sql(self, expression: exp.Cube) -> str:
    2119        expressions = self.expressions(expression, indent=False)
    2120        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
    2121
    2122    def group_sql(self, expression: exp.Group) -> str:
    2123        group_by_all = expression.args.get("all")
    2124        if group_by_all is True:
    2125            modifier = " ALL"
    2126        elif group_by_all is False:
    2127            modifier = " DISTINCT"
    2128        else:
    2129            modifier = ""
    2130
    2131        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2132
    2133        grouping_sets = self.expressions(expression, key="grouping_sets")
    2134        cube = self.expressions(expression, key="cube")
    2135        rollup = self.expressions(expression, key="rollup")
    2136
    2137        groupings = csv(
    2138            self.seg(grouping_sets) if grouping_sets else "",
    2139            self.seg(cube) if cube else "",
    2140            self.seg(rollup) if rollup else "",
    2141            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2142            sep=self.GROUPINGS_SEP,
    2143        )
    2144
    2145        if (
    2146            expression.expressions
    2147            and groupings
    2148            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2149        ):
    2150            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2151
    2152        return f"{group_by}{groupings}"
    2153
    2154    def having_sql(self, expression: exp.Having) -> str:
    2155        this = self.indent(self.sql(expression, "this"))
    2156        return f"{self.seg('HAVING')}{self.sep()}{this}"
    2157
    2158    def connect_sql(self, expression: exp.Connect) -> str:
    2159        start = self.sql(expression, "start")
    2160        start = self.seg(f"START WITH {start}") if start else ""
    2161        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2162        connect = self.sql(expression, "connect")
    2163        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2164        return start + connect
    2165
    2166    def prior_sql(self, expression: exp.Prior) -> str:
    2167        return f"PRIOR {self.sql(expression, 'this')}"
    2168
    2169    def join_sql(self, expression: exp.Join) -> str:
    2170        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2171            side = None
    2172        else:
    2173            side = expression.side
    2174
    2175        op_sql = " ".join(
    2176            op
    2177            for op in (
    2178                expression.method,
    2179                "GLOBAL" if expression.args.get("global") else None,
    2180                side,
    2181                expression.kind,
    2182                expression.hint if self.JOIN_HINTS else None,
    2183            )
    2184            if op
    2185        )
    2186        match_cond = self.sql(expression, "match_condition")
    2187        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2188        on_sql = self.sql(expression, "on")
    2189        using = expression.args.get("using")
    2190
    2191        if not on_sql and using:
    2192            on_sql = csv(*(self.sql(column) for column in using))
    2193
    2194        this = expression.this
    2195        this_sql = self.sql(this)
    2196
    2197        exprs = self.expressions(expression)
    2198        if exprs:
    2199            this_sql = f"{this_sql},{self.seg(exprs)}"
    2200
    2201        if on_sql:
    2202            on_sql = self.indent(on_sql, skip_first=True)
    2203            space = self.seg(" " * self.pad) if self.pretty else " "
    2204            if using:
    2205                on_sql = f"{space}USING ({on_sql})"
    2206            else:
    2207                on_sql = f"{space}ON {on_sql}"
    2208        elif not op_sql:
    2209            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2210                return f" {this_sql}"
    2211
    2212            return f", {this_sql}"
    2213
    2214        if op_sql != "STRAIGHT_JOIN":
    2215            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2216
    2217        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
    2218
    2219    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    2220        args = self.expressions(expression, flat=True)
    2221        args = f"({args})" if len(args.split(",")) > 1 else args
    2222        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    2223
    2224    def lateral_op(self, expression: exp.Lateral) -> str:
    2225        cross_apply = expression.args.get("cross_apply")
    2226
    2227        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2228        if cross_apply is True:
    2229            op = "INNER JOIN "
    2230        elif cross_apply is False:
    2231            op = "LEFT JOIN "
    2232        else:
    2233            op = ""
    2234
    2235        return f"{op}LATERAL"
    2236
    2237    def lateral_sql(self, expression: exp.Lateral) -> str:
    2238        this = self.sql(expression, "this")
    2239
    2240        if expression.args.get("view"):
    2241            alias = expression.args["alias"]
    2242            columns = self.expressions(alias, key="columns", flat=True)
    2243            table = f" {alias.name}" if alias.name else ""
    2244            columns = f" AS {columns}" if columns else ""
    2245            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2246            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2247
    2248        alias = self.sql(expression, "alias")
    2249        alias = f" AS {alias}" if alias else ""
    2250        return f"{self.lateral_op(expression)} {this}{alias}"
    2251
    2252    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2253        this = self.sql(expression, "this")
    2254
    2255        args = [
    2256            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2257            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2258            if e
    2259        ]
    2260
    2261        args_sql = ", ".join(self.sql(e) for e in args)
    2262        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2263        expressions = self.expressions(expression, flat=True)
    2264        expressions = f" BY {expressions}" if expressions else ""
    2265
    2266        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
    2267
    2268    def offset_sql(self, expression: exp.Offset) -> str:
    2269        this = self.sql(expression, "this")
    2270        value = expression.expression
    2271        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2272        expressions = self.expressions(expression, flat=True)
    2273        expressions = f" BY {expressions}" if expressions else ""
    2274        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
    2275
    2276    def setitem_sql(self, expression: exp.SetItem) -> str:
    2277        kind = self.sql(expression, "kind")
    2278        kind = f"{kind} " if kind else ""
    2279        this = self.sql(expression, "this")
    2280        expressions = self.expressions(expression)
    2281        collate = self.sql(expression, "collate")
    2282        collate = f" COLLATE {collate}" if collate else ""
    2283        global_ = "GLOBAL " if expression.args.get("global") else ""
    2284        return f"{global_}{kind}{this}{expressions}{collate}"
    2285
    2286    def set_sql(self, expression: exp.Set) -> str:
    2287        expressions = (
    2288            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    2289        )
    2290        tag = " TAG" if expression.args.get("tag") else ""
    2291        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
    2292
    2293    def pragma_sql(self, expression: exp.Pragma) -> str:
    2294        return f"PRAGMA {self.sql(expression, 'this')}"
    2295
    2296    def lock_sql(self, expression: exp.Lock) -> str:
    2297        if not self.LOCKING_READS_SUPPORTED:
    2298            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2299            return ""
    2300
    2301        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2302        expressions = self.expressions(expression, flat=True)
    2303        expressions = f" OF {expressions}" if expressions else ""
    2304        wait = expression.args.get("wait")
    2305
    2306        if wait is not None:
    2307            if isinstance(wait, exp.Literal):
    2308                wait = f" WAIT {self.sql(wait)}"
    2309            else:
    2310                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2311
    2312        return f"{lock_type}{expressions}{wait or ''}"
    2313
    2314    def literal_sql(self, expression: exp.Literal) -> str:
    2315        text = expression.this or ""
    2316        if expression.is_string:
    2317            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2318        return text
    2319
    2320    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2321        if self.dialect.ESCAPED_SEQUENCES:
    2322            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2323            text = "".join(
    2324                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2325            )
    2326
    2327        return self._replace_line_breaks(text).replace(
    2328            self.dialect.QUOTE_END, self._escaped_quote_end
    2329        )
    2330
    2331    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2332        local = " LOCAL" if expression.args.get("local") else ""
    2333        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2334        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2335        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2336        partition = self.sql(expression, "partition")
    2337        partition = f" {partition}" if partition else ""
    2338        input_format = self.sql(expression, "input_format")
    2339        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2340        serde = self.sql(expression, "serde")
    2341        serde = f" SERDE {serde}" if serde else ""
    2342        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    2343
    2344    def null_sql(self, *_) -> str:
    2345        return "NULL"
    2346
    2347    def boolean_sql(self, expression: exp.Boolean) -> str:
    2348        return "TRUE" if expression.this else "FALSE"
    2349
    2350    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2351        this = self.sql(expression, "this")
    2352        this = f"{this} " if this else this
    2353        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2354        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2355
    2356    def withfill_sql(self, expression: exp.WithFill) -> str:
    2357        from_sql = self.sql(expression, "from")
    2358        from_sql = f" FROM {from_sql}" if from_sql else ""
    2359        to_sql = self.sql(expression, "to")
    2360        to_sql = f" TO {to_sql}" if to_sql else ""
    2361        step_sql = self.sql(expression, "step")
    2362        step_sql = f" STEP {step_sql}" if step_sql else ""
    2363        interpolated_values = [
    2364            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2365            if isinstance(e, exp.Alias)
    2366            else self.sql(e, "this")
    2367            for e in expression.args.get("interpolate") or []
    2368        ]
    2369        interpolate = (
    2370            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2371        )
    2372        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
    2373
    2374    def cluster_sql(self, expression: exp.Cluster) -> str:
    2375        return self.op_expressions("CLUSTER BY", expression)
    2376
    2377    def distribute_sql(self, expression: exp.Distribute) -> str:
    2378        return self.op_expressions("DISTRIBUTE BY", expression)
    2379
    2380    def sort_sql(self, expression: exp.Sort) -> str:
    2381        return self.op_expressions("SORT BY", expression)
    2382
    2383    def ordered_sql(self, expression: exp.Ordered) -> str:
    2384        desc = expression.args.get("desc")
    2385        asc = not desc
    2386
    2387        nulls_first = expression.args.get("nulls_first")
    2388        nulls_last = not nulls_first
    2389        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2390        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2391        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2392
    2393        this = self.sql(expression, "this")
    2394
    2395        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2396        nulls_sort_change = ""
    2397        if nulls_first and (
    2398            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2399        ):
    2400            nulls_sort_change = " NULLS FIRST"
    2401        elif (
    2402            nulls_last
    2403            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2404            and not nulls_are_last
    2405        ):
    2406            nulls_sort_change = " NULLS LAST"
    2407
    2408        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2409        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2410            window = expression.find_ancestor(exp.Window, exp.Select)
    2411            if isinstance(window, exp.Window) and window.args.get("spec"):
    2412                self.unsupported(
    2413                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2414                )
    2415                nulls_sort_change = ""
    2416            elif self.NULL_ORDERING_SUPPORTED is False and (
    2417                (asc and nulls_sort_change == " NULLS LAST")
    2418                or (desc and nulls_sort_change == " NULLS FIRST")
    2419            ):
    2420                # BigQuery does not allow these ordering/nulls combinations when used under
    2421                # an aggregation func or under a window containing one
    2422                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2423
    2424                if isinstance(ancestor, exp.Window):
    2425                    ancestor = ancestor.this
    2426                if isinstance(ancestor, exp.AggFunc):
    2427                    self.unsupported(
    2428                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2429                    )
    2430                    nulls_sort_change = ""
    2431            elif self.NULL_ORDERING_SUPPORTED is None:
    2432                if expression.this.is_int:
    2433                    self.unsupported(
    2434                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2435                    )
    2436                elif not isinstance(expression.this, exp.Rand):
    2437                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2438                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2439                nulls_sort_change = ""
    2440
    2441        with_fill = self.sql(expression, "with_fill")
    2442        with_fill = f" {with_fill}" if with_fill else ""
    2443
    2444        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
    2445
    2446    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2447        window_frame = self.sql(expression, "window_frame")
    2448        window_frame = f"{window_frame} " if window_frame else ""
    2449
    2450        this = self.sql(expression, "this")
    2451
    2452        return f"{window_frame}{this}"
    2453
    2454    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2455        partition = self.partition_by_sql(expression)
    2456        order = self.sql(expression, "order")
    2457        measures = self.expressions(expression, key="measures")
    2458        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2459        rows = self.sql(expression, "rows")
    2460        rows = self.seg(rows) if rows else ""
    2461        after = self.sql(expression, "after")
    2462        after = self.seg(after) if after else ""
    2463        pattern = self.sql(expression, "pattern")
    2464        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2465        definition_sqls = [
    2466            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2467            for definition in expression.args.get("define", [])
    2468        ]
    2469        definitions = self.expressions(sqls=definition_sqls)
    2470        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2471        body = "".join(
    2472            (
    2473                partition,
    2474                order,
    2475                measures,
    2476                rows,
    2477                after,
    2478                pattern,
    2479                define,
    2480            )
    2481        )
    2482        alias = self.sql(expression, "alias")
    2483        alias = f" {alias}" if alias else ""
    2484        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    2485
    2486    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2487        limit = expression.args.get("limit")
    2488
    2489        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2490            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2491        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2492            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2493
    2494        return csv(
    2495            *sqls,
    2496            *[self.sql(join) for join in expression.args.get("joins") or []],
    2497            self.sql(expression, "match"),
    2498            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2499            self.sql(expression, "prewhere"),
    2500            self.sql(expression, "where"),
    2501            self.sql(expression, "connect"),
    2502            self.sql(expression, "group"),
    2503            self.sql(expression, "having"),
    2504            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2505            self.sql(expression, "order"),
    2506            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2507            *self.after_limit_modifiers(expression),
    2508            self.options_modifier(expression),
    2509            sep="",
    2510        )
    2511
    2512    def options_modifier(self, expression: exp.Expression) -> str:
    2513        options = self.expressions(expression, key="options")
    2514        return f" {options}" if options else ""
    2515
    2516    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2517        return ""
    2518
    2519    def offset_limit_modifiers(
    2520        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2521    ) -> t.List[str]:
    2522        return [
    2523            self.sql(expression, "offset") if fetch else self.sql(limit),
    2524            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2525        ]
    2526
    2527    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    2528        locks = self.expressions(expression, key="locks", sep=" ")
    2529        locks = f" {locks}" if locks else ""
    2530        return [locks, self.sql(expression, "sample")]
    2531
    2532    def select_sql(self, expression: exp.Select) -> str:
    2533        into = expression.args.get("into")
    2534        if not self.SUPPORTS_SELECT_INTO and into:
    2535            into.pop()
    2536
    2537        hint = self.sql(expression, "hint")
    2538        distinct = self.sql(expression, "distinct")
    2539        distinct = f" {distinct}" if distinct else ""
    2540        kind = self.sql(expression, "kind")
    2541
    2542        limit = expression.args.get("limit")
    2543        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2544            top = self.limit_sql(limit, top=True)
    2545            limit.pop()
    2546        else:
    2547            top = ""
    2548
    2549        expressions = self.expressions(expression)
    2550
    2551        if kind:
    2552            if kind in self.SELECT_KINDS:
    2553                kind = f" AS {kind}"
    2554            else:
    2555                if kind == "STRUCT":
    2556                    expressions = self.expressions(
    2557                        sqls=[
    2558                            self.sql(
    2559                                exp.Struct(
    2560                                    expressions=[
    2561                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2562                                        if isinstance(e, exp.Alias)
    2563                                        else e
    2564                                        for e in expression.expressions
    2565                                    ]
    2566                                )
    2567                            )
    2568                        ]
    2569                    )
    2570                kind = ""
    2571
    2572        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2573        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2574
    2575        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2576        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2577        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2578        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2579        sql = self.query_modifiers(
    2580            expression,
    2581            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2582            self.sql(expression, "into", comment=False),
    2583            self.sql(expression, "from", comment=False),
    2584        )
    2585
    2586        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2587        if expression.args.get("with"):
    2588            sql = self.maybe_comment(sql, expression)
    2589            expression.pop_comments()
    2590
    2591        sql = self.prepend_ctes(expression, sql)
    2592
    2593        if not self.SUPPORTS_SELECT_INTO and into:
    2594            if into.args.get("temporary"):
    2595                table_kind = " TEMPORARY"
    2596            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2597                table_kind = " UNLOGGED"
    2598            else:
    2599                table_kind = ""
    2600            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2601
    2602        return sql
    2603
    2604    def schema_sql(self, expression: exp.Schema) -> str:
    2605        this = self.sql(expression, "this")
    2606        sql = self.schema_columns_sql(expression)
    2607        return f"{this} {sql}" if this and sql else this or sql
    2608
    2609    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2610        if expression.expressions:
    2611            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2612        return ""
    2613
    2614    def star_sql(self, expression: exp.Star) -> str:
    2615        except_ = self.expressions(expression, key="except", flat=True)
    2616        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2617        replace = self.expressions(expression, key="replace", flat=True)
    2618        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2619        rename = self.expressions(expression, key="rename", flat=True)
    2620        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2621        return f"*{except_}{replace}{rename}"
    2622
    2623    def parameter_sql(self, expression: exp.Parameter) -> str:
    2624        this = self.sql(expression, "this")
    2625        return f"{self.PARAMETER_TOKEN}{this}"
    2626
    2627    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2628        this = self.sql(expression, "this")
    2629        kind = expression.text("kind")
    2630        if kind:
    2631            kind = f"{kind}."
    2632        return f"@@{kind}{this}"
    2633
    2634    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2635        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
    2636
    2637    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2638        alias = self.sql(expression, "alias")
    2639        alias = f"{sep}{alias}" if alias else ""
    2640        sample = self.sql(expression, "sample")
    2641        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2642            alias = f"{sample}{alias}"
    2643
    2644            # Set to None so it's not generated again by self.query_modifiers()
    2645            expression.set("sample", None)
    2646
    2647        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2648        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2649        return self.prepend_ctes(expression, sql)
    2650
    2651    def qualify_sql(self, expression: exp.Qualify) -> str:
    2652        this = self.indent(self.sql(expression, "this"))
    2653        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    2654
    2655    def unnest_sql(self, expression: exp.Unnest) -> str:
    2656        args = self.expressions(expression, flat=True)
    2657
    2658        alias = expression.args.get("alias")
    2659        offset = expression.args.get("offset")
    2660
    2661        if self.UNNEST_WITH_ORDINALITY:
    2662            if alias and isinstance(offset, exp.Expression):
    2663                alias.append("columns", offset)
    2664
    2665        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2666            columns = alias.columns
    2667            alias = self.sql(columns[0]) if columns else ""
    2668        else:
    2669            alias = self.sql(alias)
    2670
    2671        alias = f" AS {alias}" if alias else alias
    2672        if self.UNNEST_WITH_ORDINALITY:
    2673            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2674        else:
    2675            if isinstance(offset, exp.Expression):
    2676                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2677            elif offset:
    2678                suffix = f"{alias} WITH OFFSET"
    2679            else:
    2680                suffix = alias
    2681
    2682        return f"UNNEST({args}){suffix}"
    2683
    2684    def prewhere_sql(self, expression: exp.PreWhere) -> str:
    2685        return ""
    2686
    2687    def where_sql(self, expression: exp.Where) -> str:
    2688        this = self.indent(self.sql(expression, "this"))
    2689        return f"{self.seg('WHERE')}{self.sep()}{this}"
    2690
    2691    def window_sql(self, expression: exp.Window) -> str:
    2692        this = self.sql(expression, "this")
    2693        partition = self.partition_by_sql(expression)
    2694        order = expression.args.get("order")
    2695        order = self.order_sql(order, flat=True) if order else ""
    2696        spec = self.sql(expression, "spec")
    2697        alias = self.sql(expression, "alias")
    2698        over = self.sql(expression, "over") or "OVER"
    2699
    2700        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2701
    2702        first = expression.args.get("first")
    2703        if first is None:
    2704            first = ""
    2705        else:
    2706            first = "FIRST" if first else "LAST"
    2707
    2708        if not partition and not order and not spec and alias:
    2709            return f"{this} {alias}"
    2710
    2711        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2712        return f"{this} ({args})"
    2713
    2714    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2715        partition = self.expressions(expression, key="partition_by", flat=True)
    2716        return f"PARTITION BY {partition}" if partition else ""
    2717
    2718    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2719        kind = self.sql(expression, "kind")
    2720        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2721        end = (
    2722            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2723            or "CURRENT ROW"
    2724        )
    2725        return f"{kind} BETWEEN {start} AND {end}"
    2726
    2727    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2728        this = self.sql(expression, "this")
    2729        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2730        return f"{this} WITHIN GROUP ({expression_sql})"
    2731
    2732    def between_sql(self, expression: exp.Between) -> str:
    2733        this = self.sql(expression, "this")
    2734        low = self.sql(expression, "low")
    2735        high = self.sql(expression, "high")
    2736        return f"{this} BETWEEN {low} AND {high}"
    2737
    2738    def bracket_offset_expressions(
    2739        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2740    ) -> t.List[exp.Expression]:
    2741        return apply_index_offset(
    2742            expression.this,
    2743            expression.expressions,
    2744            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2745        )
    2746
    2747    def bracket_sql(self, expression: exp.Bracket) -> str:
    2748        expressions = self.bracket_offset_expressions(expression)
    2749        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2750        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    2751
    2752    def all_sql(self, expression: exp.All) -> str:
    2753        return f"ALL {self.wrap(expression)}"
    2754
    2755    def any_sql(self, expression: exp.Any) -> str:
    2756        this = self.sql(expression, "this")
    2757        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2758            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2759                this = self.wrap(this)
    2760            return f"ANY{this}"
    2761        return f"ANY {this}"
    2762
    2763    def exists_sql(self, expression: exp.Exists) -> str:
    2764        return f"EXISTS{self.wrap(expression)}"
    2765
    2766    def case_sql(self, expression: exp.Case) -> str:
    2767        this = self.sql(expression, "this")
    2768        statements = [f"CASE {this}" if this else "CASE"]
    2769
    2770        for e in expression.args["ifs"]:
    2771            statements.append(f"WHEN {self.sql(e, 'this')}")
    2772            statements.append(f"THEN {self.sql(e, 'true')}")
    2773
    2774        default = self.sql(expression, "default")
    2775
    2776        if default:
    2777            statements.append(f"ELSE {default}")
    2778
    2779        statements.append("END")
    2780
    2781        if self.pretty and self.too_wide(statements):
    2782            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2783
    2784        return " ".join(statements)
    2785
    2786    def constraint_sql(self, expression: exp.Constraint) -> str:
    2787        this = self.sql(expression, "this")
    2788        expressions = self.expressions(expression, flat=True)
    2789        return f"CONSTRAINT {this} {expressions}"
    2790
    2791    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2792        order = expression.args.get("order")
    2793        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2794        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    2795
    2796    def extract_sql(self, expression: exp.Extract) -> str:
    2797        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2798        expression_sql = self.sql(expression, "expression")
    2799        return f"EXTRACT({this} FROM {expression_sql})"
    2800
    2801    def trim_sql(self, expression: exp.Trim) -> str:
    2802        trim_type = self.sql(expression, "position")
    2803
    2804        if trim_type == "LEADING":
    2805            func_name = "LTRIM"
    2806        elif trim_type == "TRAILING":
    2807            func_name = "RTRIM"
    2808        else:
    2809            func_name = "TRIM"
    2810
    2811        return self.func(func_name, expression.this, expression.expression)
    2812
    2813    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2814        args = expression.expressions
    2815        if isinstance(expression, exp.ConcatWs):
    2816            args = args[1:]  # Skip the delimiter
    2817
    2818        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2819            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2820
    2821        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2822            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2823
    2824        return args
    2825
    2826    def concat_sql(self, expression: exp.Concat) -> str:
    2827        expressions = self.convert_concat_args(expression)
    2828
    2829        # Some dialects don't allow a single-argument CONCAT call
    2830        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2831            return self.sql(expressions[0])
    2832
    2833        return self.func("CONCAT", *expressions)
    2834
    2835    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2836        return self.func(
    2837            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2838        )
    2839
    2840    def check_sql(self, expression: exp.Check) -> str:
    2841        this = self.sql(expression, key="this")
    2842        return f"CHECK ({this})"
    2843
    2844    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2845        expressions = self.expressions(expression, flat=True)
    2846        expressions = f" ({expressions})" if expressions else ""
    2847        reference = self.sql(expression, "reference")
    2848        reference = f" {reference}" if reference else ""
    2849        delete = self.sql(expression, "delete")
    2850        delete = f" ON DELETE {delete}" if delete else ""
    2851        update = self.sql(expression, "update")
    2852        update = f" ON UPDATE {update}" if update else ""
    2853        return f"FOREIGN KEY{expressions}{reference}{delete}{update}"
    2854
    2855    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2856        expressions = self.expressions(expression, flat=True)
    2857        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2858        options = f" {options}" if options else ""
    2859        return f"PRIMARY KEY ({expressions}){options}"
    2860
    2861    def if_sql(self, expression: exp.If) -> str:
    2862        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
    2863
    2864    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2865        modifier = expression.args.get("modifier")
    2866        modifier = f" {modifier}" if modifier else ""
    2867        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    2868
    2869    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2870        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
    2871
    2872    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2873        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2874
    2875        if expression.args.get("escape"):
    2876            path = self.escape_str(path)
    2877
    2878        if self.QUOTE_JSON_PATH:
    2879            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2880
    2881        return path
    2882
    2883    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2884        if isinstance(expression, exp.JSONPathPart):
    2885            transform = self.TRANSFORMS.get(expression.__class__)
    2886            if not callable(transform):
    2887                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2888                return ""
    2889
    2890            return transform(self, expression)
    2891
    2892        if isinstance(expression, int):
    2893            return str(expression)
    2894
    2895        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2896            escaped = expression.replace("'", "\\'")
    2897            escaped = f"\\'{expression}\\'"
    2898        else:
    2899            escaped = expression.replace('"', '\\"')
    2900            escaped = f'"{escaped}"'
    2901
    2902        return escaped
    2903
    2904    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2905        return f"{self.sql(expression, 'this')} FORMAT JSON"
    2906
    2907    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2908        null_handling = expression.args.get("null_handling")
    2909        null_handling = f" {null_handling}" if null_handling else ""
    2910
    2911        unique_keys = expression.args.get("unique_keys")
    2912        if unique_keys is not None:
    2913            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2914        else:
    2915            unique_keys = ""
    2916
    2917        return_type = self.sql(expression, "return_type")
    2918        return_type = f" RETURNING {return_type}" if return_type else ""
    2919        encoding = self.sql(expression, "encoding")
    2920        encoding = f" ENCODING {encoding}" if encoding else ""
    2921
    2922        return self.func(
    2923            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2924            *expression.expressions,
    2925            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2926        )
    2927
    2928    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
    2929        return self.jsonobject_sql(expression)
    2930
    2931    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2932        null_handling = expression.args.get("null_handling")
    2933        null_handling = f" {null_handling}" if null_handling else ""
    2934        return_type = self.sql(expression, "return_type")
    2935        return_type = f" RETURNING {return_type}" if return_type else ""
    2936        strict = " STRICT" if expression.args.get("strict") else ""
    2937        return self.func(
    2938            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2939        )
    2940
    2941    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2942        this = self.sql(expression, "this")
    2943        order = self.sql(expression, "order")
    2944        null_handling = expression.args.get("null_handling")
    2945        null_handling = f" {null_handling}" if null_handling else ""
    2946        return_type = self.sql(expression, "return_type")
    2947        return_type = f" RETURNING {return_type}" if return_type else ""
    2948        strict = " STRICT" if expression.args.get("strict") else ""
    2949        return self.func(
    2950            "JSON_ARRAYAGG",
    2951            this,
    2952            suffix=f"{order}{null_handling}{return_type}{strict})",
    2953        )
    2954
    2955    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2956        path = self.sql(expression, "path")
    2957        path = f" PATH {path}" if path else ""
    2958        nested_schema = self.sql(expression, "nested_schema")
    2959
    2960        if nested_schema:
    2961            return f"NESTED{path} {nested_schema}"
    2962
    2963        this = self.sql(expression, "this")
    2964        kind = self.sql(expression, "kind")
    2965        kind = f" {kind}" if kind else ""
    2966        return f"{this}{kind}{path}"
    2967
    2968    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    2969        return self.func("COLUMNS", *expression.expressions)
    2970
    2971    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2972        this = self.sql(expression, "this")
    2973        path = self.sql(expression, "path")
    2974        path = f", {path}" if path else ""
    2975        error_handling = expression.args.get("error_handling")
    2976        error_handling = f" {error_handling}" if error_handling else ""
    2977        empty_handling = expression.args.get("empty_handling")
    2978        empty_handling = f" {empty_handling}" if empty_handling else ""
    2979        schema = self.sql(expression, "schema")
    2980        return self.func(
    2981            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2982        )
    2983
    2984    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2985        this = self.sql(expression, "this")
    2986        kind = self.sql(expression, "kind")
    2987        path = self.sql(expression, "path")
    2988        path = f" {path}" if path else ""
    2989        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2990        return f"{this} {kind}{path}{as_json}"
    2991
    2992    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2993        this = self.sql(expression, "this")
    2994        path = self.sql(expression, "path")
    2995        path = f", {path}" if path else ""
    2996        expressions = self.expressions(expression)
    2997        with_ = (
    2998            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2999            if expressions
    3000            else ""
    3001        )
    3002        return f"OPENJSON({this}{path}){with_}"
    3003
    3004    def in_sql(self, expression: exp.In) -> str:
    3005        query = expression.args.get("query")
    3006        unnest = expression.args.get("unnest")
    3007        field = expression.args.get("field")
    3008        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3009
    3010        if query:
    3011            in_sql = self.sql(query)
    3012        elif unnest:
    3013            in_sql = self.in_unnest_op(unnest)
    3014        elif field:
    3015            in_sql = self.sql(field)
    3016        else:
    3017            in_sql = f"({self.expressions(expression, flat=True)})"
    3018
    3019        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    3020
    3021    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    3022        return f"(SELECT {self.sql(unnest)})"
    3023
    3024    def interval_sql(self, expression: exp.Interval) -> str:
    3025        unit = self.sql(expression, "unit")
    3026        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3027            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3028        unit = f" {unit}" if unit else ""
    3029
    3030        if self.SINGLE_STRING_INTERVAL:
    3031            this = expression.this.name if expression.this else ""
    3032            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    3033
    3034        this = self.sql(expression, "this")
    3035        if this:
    3036            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3037            this = f" {this}" if unwrapped else f" ({this})"
    3038
    3039        return f"INTERVAL{this}{unit}"
    3040
    3041    def return_sql(self, expression: exp.Return) -> str:
    3042        return f"RETURN {self.sql(expression, 'this')}"
    3043
    3044    def reference_sql(self, expression: exp.Reference) -> str:
    3045        this = self.sql(expression, "this")
    3046        expressions = self.expressions(expression, flat=True)
    3047        expressions = f"({expressions})" if expressions else ""
    3048        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3049        options = f" {options}" if options else ""
    3050        return f"REFERENCES {this}{expressions}{options}"
    3051
    3052    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3053        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3054        parent = expression.parent
    3055        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3056        return self.func(
    3057            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3058        )
    3059
    3060    def paren_sql(self, expression: exp.Paren) -> str:
    3061        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    3062        return f"({sql}{self.seg(')', sep='')}"
    3063
    3064    def neg_sql(self, expression: exp.Neg) -> str:
    3065        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    3066        this_sql = self.sql(expression, "this")
    3067        sep = " " if this_sql[0] == "-" else ""
    3068        return f"-{sep}{this_sql}"
    3069
    3070    def not_sql(self, expression: exp.Not) -> str:
    3071        return f"NOT {self.sql(expression, 'this')}"
    3072
    3073    def alias_sql(self, expression: exp.Alias) -> str:
    3074        alias = self.sql(expression, "alias")
    3075        alias = f" AS {alias}" if alias else ""
    3076        return f"{self.sql(expression, 'this')}{alias}"
    3077
    3078    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3079        alias = expression.args["alias"]
    3080
    3081        identifier_alias = isinstance(alias, exp.Identifier)
    3082        literal_alias = isinstance(alias, exp.Literal)
    3083
    3084        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3085            alias.replace(exp.Literal.string(alias.output_name))
    3086        elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3087            alias.replace(exp.to_identifier(alias.output_name))
    3088
    3089        return self.alias_sql(expression)
    3090
    3091    def aliases_sql(self, expression: exp.Aliases) -> str:
    3092        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    3093
    3094    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3095        this = self.sql(expression, "this")
    3096        index = self.sql(expression, "expression")
    3097        return f"{this} AT {index}"
    3098
    3099    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3100        this = self.sql(expression, "this")
    3101        zone = self.sql(expression, "zone")
    3102        return f"{this} AT TIME ZONE {zone}"
    3103
    3104    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3105        this = self.sql(expression, "this")
    3106        zone = self.sql(expression, "zone")
    3107        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
    3108
    3109    def add_sql(self, expression: exp.Add) -> str:
    3110        return self.binary(expression, "+")
    3111
    3112    def and_sql(
    3113        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3114    ) -> str:
    3115        return self.connector_sql(expression, "AND", stack)
    3116
    3117    def or_sql(
    3118        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3119    ) -> str:
    3120        return self.connector_sql(expression, "OR", stack)
    3121
    3122    def xor_sql(
    3123        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3124    ) -> str:
    3125        return self.connector_sql(expression, "XOR", stack)
    3126
    3127    def connector_sql(
    3128        self,
    3129        expression: exp.Connector,
    3130        op: str,
    3131        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3132    ) -> str:
    3133        if stack is not None:
    3134            if expression.expressions:
    3135                stack.append(self.expressions(expression, sep=f" {op} "))
    3136            else:
    3137                stack.append(expression.right)
    3138                if expression.comments and self.comments:
    3139                    for comment in expression.comments:
    3140                        if comment:
    3141                            op += f" /*{self.pad_comment(comment)}*/"
    3142                stack.extend((op, expression.left))
    3143            return op
    3144
    3145        stack = [expression]
    3146        sqls: t.List[str] = []
    3147        ops = set()
    3148
    3149        while stack:
    3150            node = stack.pop()
    3151            if isinstance(node, exp.Connector):
    3152                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3153            else:
    3154                sql = self.sql(node)
    3155                if sqls and sqls[-1] in ops:
    3156                    sqls[-1] += f" {sql}"
    3157                else:
    3158                    sqls.append(sql)
    3159
    3160        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3161        return sep.join(sqls)
    3162
    3163    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    3164        return self.binary(expression, "&")
    3165
    3166    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    3167        return self.binary(expression, "<<")
    3168
    3169    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    3170        return f"~{self.sql(expression, 'this')}"
    3171
    3172    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    3173        return self.binary(expression, "|")
    3174
    3175    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    3176        return self.binary(expression, ">>")
    3177
    3178    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    3179        return self.binary(expression, "^")
    3180
    3181    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3182        format_sql = self.sql(expression, "format")
    3183        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3184        to_sql = self.sql(expression, "to")
    3185        to_sql = f" {to_sql}" if to_sql else ""
    3186        action = self.sql(expression, "action")
    3187        action = f" {action}" if action else ""
    3188        default = self.sql(expression, "default")
    3189        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3190        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
    3191
    3192    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    3193        zone = self.sql(expression, "this")
    3194        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    3195
    3196    def collate_sql(self, expression: exp.Collate) -> str:
    3197        if self.COLLATE_IS_FUNC:
    3198            return self.function_fallback_sql(expression)
    3199        return self.binary(expression, "COLLATE")
    3200
    3201    def command_sql(self, expression: exp.Command) -> str:
    3202        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
    3203
    3204    def comment_sql(self, expression: exp.Comment) -> str:
    3205        this = self.sql(expression, "this")
    3206        kind = expression.args["kind"]
    3207        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3208        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3209        expression_sql = self.sql(expression, "expression")
    3210        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
    3211
    3212    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3213        this = self.sql(expression, "this")
    3214        delete = " DELETE" if expression.args.get("delete") else ""
    3215        recompress = self.sql(expression, "recompress")
    3216        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3217        to_disk = self.sql(expression, "to_disk")
    3218        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3219        to_volume = self.sql(expression, "to_volume")
    3220        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3221        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    3222
    3223    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3224        where = self.sql(expression, "where")
    3225        group = self.sql(expression, "group")
    3226        aggregates = self.expressions(expression, key="aggregates")
    3227        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3228
    3229        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3230            return f"TTL {self.expressions(expression, flat=True)}"
    3231
    3232        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    3233
    3234    def transaction_sql(self, expression: exp.Transaction) -> str:
    3235        return "BEGIN"
    3236
    3237    def commit_sql(self, expression: exp.Commit) -> str:
    3238        chain = expression.args.get("chain")
    3239        if chain is not None:
    3240            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3241
    3242        return f"COMMIT{chain or ''}"
    3243
    3244    def rollback_sql(self, expression: exp.Rollback) -> str:
    3245        savepoint = expression.args.get("savepoint")
    3246        savepoint = f" TO {savepoint}" if savepoint else ""
    3247        return f"ROLLBACK{savepoint}"
    3248
    3249    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3250        this = self.sql(expression, "this")
    3251
    3252        dtype = self.sql(expression, "dtype")
    3253        if dtype:
    3254            collate = self.sql(expression, "collate")
    3255            collate = f" COLLATE {collate}" if collate else ""
    3256            using = self.sql(expression, "using")
    3257            using = f" USING {using}" if using else ""
    3258            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
    3259
    3260        default = self.sql(expression, "default")
    3261        if default:
    3262            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3263
    3264        comment = self.sql(expression, "comment")
    3265        if comment:
    3266            return f"ALTER COLUMN {this} COMMENT {comment}"
    3267
    3268        allow_null = expression.args.get("allow_null")
    3269        drop = expression.args.get("drop")
    3270
    3271        if not drop and not allow_null:
    3272            self.unsupported("Unsupported ALTER COLUMN syntax")
    3273
    3274        if allow_null is not None:
    3275            keyword = "DROP" if drop else "SET"
    3276            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3277
    3278        return f"ALTER COLUMN {this} DROP DEFAULT"
    3279
    3280    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3281        this = self.sql(expression, "this")
    3282        if not isinstance(expression.this, exp.Var):
    3283            this = f"KEY DISTKEY {this}"
    3284        return f"ALTER DISTSTYLE {this}"
    3285
    3286    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3287        compound = " COMPOUND" if expression.args.get("compound") else ""
    3288        this = self.sql(expression, "this")
    3289        expressions = self.expressions(expression, flat=True)
    3290        expressions = f"({expressions})" if expressions else ""
    3291        return f"ALTER{compound} SORTKEY {this or expressions}"
    3292
    3293    def alterrename_sql(self, expression: exp.AlterRename) -> str:
    3294        if not self.RENAME_TABLE_WITH_DB:
    3295            # Remove db from tables
    3296            expression = expression.transform(
    3297                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3298            ).assert_is(exp.AlterRename)
    3299        this = self.sql(expression, "this")
    3300        return f"RENAME TO {this}"
    3301
    3302    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3303        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3304        old_column = self.sql(expression, "this")
    3305        new_column = self.sql(expression, "to")
    3306        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
    3307
    3308    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3309        exprs = self.expressions(expression, flat=True)
    3310        return f"SET {exprs}"
    3311
    3312    def alter_sql(self, expression: exp.Alter) -> str:
    3313        actions = expression.args["actions"]
    3314
    3315        if isinstance(actions[0], exp.ColumnDef):
    3316            actions = self.add_column_sql(expression)
    3317        elif isinstance(actions[0], exp.Schema):
    3318            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3319        elif isinstance(actions[0], exp.Delete):
    3320            actions = self.expressions(expression, key="actions", flat=True)
    3321        elif isinstance(actions[0], exp.Query):
    3322            actions = "AS " + self.expressions(expression, key="actions")
    3323        else:
    3324            actions = self.expressions(expression, key="actions", flat=True)
    3325
    3326        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3327        on_cluster = self.sql(expression, "cluster")
    3328        on_cluster = f" {on_cluster}" if on_cluster else ""
    3329        only = " ONLY" if expression.args.get("only") else ""
    3330        options = self.expressions(expression, key="options")
    3331        options = f", {options}" if options else ""
    3332        kind = self.sql(expression, "kind")
    3333        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3334
    3335        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
    3336
    3337    def add_column_sql(self, expression: exp.Alter) -> str:
    3338        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3339            return self.expressions(
    3340                expression,
    3341                key="actions",
    3342                prefix="ADD COLUMN ",
    3343                skip_first=True,
    3344            )
    3345        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
    3346
    3347    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3348        expressions = self.expressions(expression)
    3349        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3350        return f"DROP{exists}{expressions}"
    3351
    3352    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3353        return f"ADD {self.expressions(expression)}"
    3354
    3355    def distinct_sql(self, expression: exp.Distinct) -> str:
    3356        this = self.expressions(expression, flat=True)
    3357
    3358        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3359            case = exp.case()
    3360            for arg in expression.expressions:
    3361                case = case.when(arg.is_(exp.null()), exp.null())
    3362            this = self.sql(case.else_(f"({this})"))
    3363
    3364        this = f" {this}" if this else ""
    3365
    3366        on = self.sql(expression, "on")
    3367        on = f" ON {on}" if on else ""
    3368        return f"DISTINCT{this}{on}"
    3369
    3370    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    3371        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
    3372
    3373    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    3374        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
    3375
    3376    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3377        this_sql = self.sql(expression, "this")
    3378        expression_sql = self.sql(expression, "expression")
    3379        kind = "MAX" if expression.args.get("max") else "MIN"
    3380        return f"{this_sql} HAVING {kind} {expression_sql}"
    3381
    3382    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3383        return self.sql(
    3384            exp.Cast(
    3385                this=exp.Div(this=expression.this, expression=expression.expression),
    3386                to=exp.DataType(this=exp.DataType.Type.INT),
    3387            )
    3388        )
    3389
    3390    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3391        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3392            return self.func(
    3393                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3394            )
    3395        return self.binary(expression, "||")
    3396
    3397    def div_sql(self, expression: exp.Div) -> str:
    3398        l, r = expression.left, expression.right
    3399
    3400        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3401            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3402
    3403        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3404            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3405                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3406
    3407        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3408            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3409                return self.sql(
    3410                    exp.cast(
    3411                        l / r,
    3412                        to=exp.DataType.Type.BIGINT,
    3413                    )
    3414                )
    3415
    3416        return self.binary(expression, "/")
    3417
    3418    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3419        n = exp._wrap(expression.this, exp.Binary)
    3420        d = exp._wrap(expression.expression, exp.Binary)
    3421        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
    3422
    3423    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    3424        return self.binary(expression, "OVERLAPS")
    3425
    3426    def distance_sql(self, expression: exp.Distance) -> str:
    3427        return self.binary(expression, "<->")
    3428
    3429    def dot_sql(self, expression: exp.Dot) -> str:
    3430        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    3431
    3432    def eq_sql(self, expression: exp.EQ) -> str:
    3433        return self.binary(expression, "=")
    3434
    3435    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    3436        return self.binary(expression, ":=")
    3437
    3438    def escape_sql(self, expression: exp.Escape) -> str:
    3439        return self.binary(expression, "ESCAPE")
    3440
    3441    def glob_sql(self, expression: exp.Glob) -> str:
    3442        return self.binary(expression, "GLOB")
    3443
    3444    def gt_sql(self, expression: exp.GT) -> str:
    3445        return self.binary(expression, ">")
    3446
    3447    def gte_sql(self, expression: exp.GTE) -> str:
    3448        return self.binary(expression, ">=")
    3449
    3450    def ilike_sql(self, expression: exp.ILike) -> str:
    3451        return self.binary(expression, "ILIKE")
    3452
    3453    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    3454        return self.binary(expression, "ILIKE ANY")
    3455
    3456    def is_sql(self, expression: exp.Is) -> str:
    3457        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3458            return self.sql(
    3459                expression.this if expression.expression.this else exp.not_(expression.this)
    3460            )
    3461        return self.binary(expression, "IS")
    3462
    3463    def like_sql(self, expression: exp.Like) -> str:
    3464        return self.binary(expression, "LIKE")
    3465
    3466    def likeany_sql(self, expression: exp.LikeAny) -> str:
    3467        return self.binary(expression, "LIKE ANY")
    3468
    3469    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    3470        return self.binary(expression, "SIMILAR TO")
    3471
    3472    def lt_sql(self, expression: exp.LT) -> str:
    3473        return self.binary(expression, "<")
    3474
    3475    def lte_sql(self, expression: exp.LTE) -> str:
    3476        return self.binary(expression, "<=")
    3477
    3478    def mod_sql(self, expression: exp.Mod) -> str:
    3479        return self.binary(expression, "%")
    3480
    3481    def mul_sql(self, expression: exp.Mul) -> str:
    3482        return self.binary(expression, "*")
    3483
    3484    def neq_sql(self, expression: exp.NEQ) -> str:
    3485        return self.binary(expression, "<>")
    3486
    3487    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    3488        return self.binary(expression, "IS NOT DISTINCT FROM")
    3489
    3490    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    3491        return self.binary(expression, "IS DISTINCT FROM")
    3492
    3493    def slice_sql(self, expression: exp.Slice) -> str:
    3494        return self.binary(expression, ":")
    3495
    3496    def sub_sql(self, expression: exp.Sub) -> str:
    3497        return self.binary(expression, "-")
    3498
    3499    def trycast_sql(self, expression: exp.TryCast) -> str:
    3500        return self.cast_sql(expression, safe_prefix="TRY_")
    3501
    3502    def try_sql(self, expression: exp.Try) -> str:
    3503        if not self.TRY_SUPPORTED:
    3504            self.unsupported("Unsupported TRY function")
    3505            return self.sql(expression, "this")
    3506
    3507        return self.func("TRY", expression.this)
    3508
    3509    def log_sql(self, expression: exp.Log) -> str:
    3510        this = expression.this
    3511        expr = expression.expression
    3512
    3513        if self.dialect.LOG_BASE_FIRST is False:
    3514            this, expr = expr, this
    3515        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3516            if this.name in ("2", "10"):
    3517                return self.func(f"LOG{this.name}", expr)
    3518
    3519            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3520
    3521        return self.func("LOG", this, expr)
    3522
    3523    def use_sql(self, expression: exp.Use) -> str:
    3524        kind = self.sql(expression, "kind")
    3525        kind = f" {kind}" if kind else ""
    3526        this = self.sql(expression, "this")
    3527        this = f" {this}" if this else ""
    3528        return f"USE{kind}{this}"
    3529
    3530    def binary(self, expression: exp.Binary, op: str) -> str:
    3531        sqls: t.List[str] = []
    3532        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3533        binary_type = type(expression)
    3534
    3535        while stack:
    3536            node = stack.pop()
    3537
    3538            if type(node) is binary_type:
    3539                op_func = node.args.get("operator")
    3540                if op_func:
    3541                    op = f"OPERATOR({self.sql(op_func)})"
    3542
    3543                stack.append(node.right)
    3544                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3545                stack.append(node.left)
    3546            else:
    3547                sqls.append(self.sql(node))
    3548
    3549        return "".join(sqls)
    3550
    3551    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3552        to_clause = self.sql(expression, "to")
    3553        if to_clause:
    3554            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3555
    3556        return self.function_fallback_sql(expression)
    3557
    3558    def function_fallback_sql(self, expression: exp.Func) -> str:
    3559        args = []
    3560
    3561        for key in expression.arg_types:
    3562            arg_value = expression.args.get(key)
    3563
    3564            if isinstance(arg_value, list):
    3565                for value in arg_value:
    3566                    args.append(value)
    3567            elif arg_value is not None:
    3568                args.append(arg_value)
    3569
    3570        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3571            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3572        else:
    3573            name = expression.sql_name()
    3574
    3575        return self.func(name, *args)
    3576
    3577    def func(
    3578        self,
    3579        name: str,
    3580        *args: t.Optional[exp.Expression | str],
    3581        prefix: str = "(",
    3582        suffix: str = ")",
    3583        normalize: bool = True,
    3584    ) -> str:
    3585        name = self.normalize_func(name) if normalize else name
    3586        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
    3587
    3588    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3589        arg_sqls = tuple(
    3590            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3591        )
    3592        if self.pretty and self.too_wide(arg_sqls):
    3593            return self.indent(
    3594                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3595            )
    3596        return sep.join(arg_sqls)
    3597
    3598    def too_wide(self, args: t.Iterable) -> bool:
    3599        return sum(len(arg) for arg in args) > self.max_text_width
    3600
    3601    def format_time(
    3602        self,
    3603        expression: exp.Expression,
    3604        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3605        inverse_time_trie: t.Optional[t.Dict] = None,
    3606    ) -> t.Optional[str]:
    3607        return format_time(
    3608            self.sql(expression, "format"),
    3609            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3610            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3611        )
    3612
    3613    def expressions(
    3614        self,
    3615        expression: t.Optional[exp.Expression] = None,
    3616        key: t.Optional[str] = None,
    3617        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3618        flat: bool = False,
    3619        indent: bool = True,
    3620        skip_first: bool = False,
    3621        skip_last: bool = False,
    3622        sep: str = ", ",
    3623        prefix: str = "",
    3624        dynamic: bool = False,
    3625        new_line: bool = False,
    3626    ) -> str:
    3627        expressions = expression.args.get(key or "expressions") if expression else sqls
    3628
    3629        if not expressions:
    3630            return ""
    3631
    3632        if flat:
    3633            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3634
    3635        num_sqls = len(expressions)
    3636        result_sqls = []
    3637
    3638        for i, e in enumerate(expressions):
    3639            sql = self.sql(e, comment=False)
    3640            if not sql:
    3641                continue
    3642
    3643            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3644
    3645            if self.pretty:
    3646                if self.leading_comma:
    3647                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3648                else:
    3649                    result_sqls.append(
    3650                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3651                    )
    3652            else:
    3653                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3654
    3655        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3656            if new_line:
    3657                result_sqls.insert(0, "")
    3658                result_sqls.append("")
    3659            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3660        else:
    3661            result_sql = "".join(result_sqls)
    3662
    3663        return (
    3664            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3665            if indent
    3666            else result_sql
    3667        )
    3668
    3669    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3670        flat = flat or isinstance(expression.parent, exp.Properties)
    3671        expressions_sql = self.expressions(expression, flat=flat)
    3672        if flat:
    3673            return f"{op} {expressions_sql}"
    3674        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    3675
    3676    def naked_property(self, expression: exp.Property) -> str:
    3677        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3678        if not property_name:
    3679            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3680        return f"{property_name} {self.sql(expression, 'this')}"
    3681
    3682    def tag_sql(self, expression: exp.Tag) -> str:
    3683        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    3684
    3685    def token_sql(self, token_type: TokenType) -> str:
    3686        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    3687
    3688    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3689        this = self.sql(expression, "this")
    3690        expressions = self.no_identify(self.expressions, expression)
    3691        expressions = (
    3692            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3693        )
    3694        return f"{this}{expressions}" if expressions.strip() != "" else this
    3695
    3696    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3697        this = self.sql(expression, "this")
    3698        expressions = self.expressions(expression, flat=True)
    3699        return f"{this}({expressions})"
    3700
    3701    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    3702        return self.binary(expression, "=>")
    3703
    3704    def when_sql(self, expression: exp.When) -> str:
    3705        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3706        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3707        condition = self.sql(expression, "condition")
    3708        condition = f" AND {condition}" if condition else ""
    3709
    3710        then_expression = expression.args.get("then")
    3711        if isinstance(then_expression, exp.Insert):
    3712            this = self.sql(then_expression, "this")
    3713            this = f"INSERT {this}" if this else "INSERT"
    3714            then = self.sql(then_expression, "expression")
    3715            then = f"{this} VALUES {then}" if then else this
    3716        elif isinstance(then_expression, exp.Update):
    3717            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3718                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3719            else:
    3720                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3721        else:
    3722            then = self.sql(then_expression)
    3723        return f"WHEN {matched}{source}{condition} THEN {then}"
    3724
    3725    def whens_sql(self, expression: exp.Whens) -> str:
    3726        return self.expressions(expression, sep=" ", indent=False)
    3727
    3728    def merge_sql(self, expression: exp.Merge) -> str:
    3729        table = expression.this
    3730        table_alias = ""
    3731
    3732        hints = table.args.get("hints")
    3733        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3734            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3735            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3736
    3737        this = self.sql(table)
    3738        using = f"USING {self.sql(expression, 'using')}"
    3739        on = f"ON {self.sql(expression, 'on')}"
    3740        whens = self.sql(expression, "whens")
    3741
    3742        returning = self.sql(expression, "returning")
    3743        if returning:
    3744            whens = f"{whens}{returning}"
    3745
    3746        sep = self.sep()
    3747
    3748        return self.prepend_ctes(
    3749            expression,
    3750            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    3751        )
    3752
    3753    @unsupported_args("format")
    3754    def tochar_sql(self, expression: exp.ToChar) -> str:
    3755        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
    3756
    3757    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3758        if not self.SUPPORTS_TO_NUMBER:
    3759            self.unsupported("Unsupported TO_NUMBER function")
    3760            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3761
    3762        fmt = expression.args.get("format")
    3763        if not fmt:
    3764            self.unsupported("Conversion format is required for TO_NUMBER")
    3765            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3766
    3767        return self.func("TO_NUMBER", expression.this, fmt)
    3768
    3769    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3770        this = self.sql(expression, "this")
    3771        kind = self.sql(expression, "kind")
    3772        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3773        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3774        return f"{this}({kind}{args})"
    3775
    3776    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3777        this = self.sql(expression, "this")
    3778        max = self.sql(expression, "max")
    3779        min = self.sql(expression, "min")
    3780        return f"{this}(MIN {min} MAX {max})"
    3781
    3782    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    3783        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    3784
    3785    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
    3786        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
    3787
    3788    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
    3789    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
    3790        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
    3791
    3792    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
    3793    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    3794        expressions = self.expressions(expression, flat=True)
    3795        expressions = f" {self.wrap(expressions)}" if expressions else ""
    3796        buckets = self.sql(expression, "buckets")
    3797        kind = self.sql(expression, "kind")
    3798        buckets = f" BUCKETS {buckets}" if buckets else ""
    3799        order = self.sql(expression, "order")
    3800        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
    3801
    3802    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    3803        return ""
    3804
    3805    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3806        expressions = self.expressions(expression, key="expressions", flat=True)
    3807        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3808        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3809        buckets = self.sql(expression, "buckets")
    3810        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
    3811
    3812    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3813        this = self.sql(expression, "this")
    3814        having = self.sql(expression, "having")
    3815
    3816        if having:
    3817            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3818
    3819        return self.func("ANY_VALUE", this)
    3820
    3821    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3822        transform = self.func("TRANSFORM", *expression.expressions)
    3823        row_format_before = self.sql(expression, "row_format_before")
    3824        row_format_before = f" {row_format_before}" if row_format_before else ""
    3825        record_writer = self.sql(expression, "record_writer")
    3826        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3827        using = f" USING {self.sql(expression, 'command_script')}"
    3828        schema = self.sql(expression, "schema")
    3829        schema = f" AS {schema}" if schema else ""
    3830        row_format_after = self.sql(expression, "row_format_after")
    3831        row_format_after = f" {row_format_after}" if row_format_after else ""
    3832        record_reader = self.sql(expression, "record_reader")
    3833        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3834        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
    3835
    3836    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3837        key_block_size = self.sql(expression, "key_block_size")
    3838        if key_block_size:
    3839            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3840
    3841        using = self.sql(expression, "using")
    3842        if using:
    3843            return f"USING {using}"
    3844
    3845        parser = self.sql(expression, "parser")
    3846        if parser:
    3847            return f"WITH PARSER {parser}"
    3848
    3849        comment = self.sql(expression, "comment")
    3850        if comment:
    3851            return f"COMMENT {comment}"
    3852
    3853        visible = expression.args.get("visible")
    3854        if visible is not None:
    3855            return "VISIBLE" if visible else "INVISIBLE"
    3856
    3857        engine_attr = self.sql(expression, "engine_attr")
    3858        if engine_attr:
    3859            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3860
    3861        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3862        if secondary_engine_attr:
    3863            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3864
    3865        self.unsupported("Unsupported index constraint option.")
    3866        return ""
    3867
    3868    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
    3869        enforced = " ENFORCED" if expression.args.get("enforced") else ""
    3870        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
    3871
    3872    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3873        kind = self.sql(expression, "kind")
    3874        kind = f"{kind} INDEX" if kind else "INDEX"
    3875        this = self.sql(expression, "this")
    3876        this = f" {this}" if this else ""
    3877        index_type = self.sql(expression, "index_type")
    3878        index_type = f" USING {index_type}" if index_type else ""
    3879        expressions = self.expressions(expression, flat=True)
    3880        expressions = f" ({expressions})" if expressions else ""
    3881        options = self.expressions(expression, key="options", sep=" ")
    3882        options = f" {options}" if options else ""
    3883        return f"{kind}{this}{index_type}{expressions}{options}"
    3884
    3885    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3886        if self.NVL2_SUPPORTED:
    3887            return self.function_fallback_sql(expression)
    3888
    3889        case = exp.Case().when(
    3890            expression.this.is_(exp.null()).not_(copy=False),
    3891            expression.args["true"],
    3892            copy=False,
    3893        )
    3894        else_cond = expression.args.get("false")
    3895        if else_cond:
    3896            case.else_(else_cond, copy=False)
    3897
    3898        return self.sql(case)
    3899
    3900    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3901        this = self.sql(expression, "this")
    3902        expr = self.sql(expression, "expression")
    3903        iterator = self.sql(expression, "iterator")
    3904        condition = self.sql(expression, "condition")
    3905        condition = f" IF {condition}" if condition else ""
    3906        return f"{this} FOR {expr} IN {iterator}{condition}"
    3907
    3908    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    3909        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
    3910
    3911    def opclass_sql(self, expression: exp.Opclass) -> str:
    3912        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    3913
    3914    def predict_sql(self, expression: exp.Predict) -> str:
    3915        model = self.sql(expression, "this")
    3916        model = f"MODEL {model}"
    3917        table = self.sql(expression, "expression")
    3918        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3919        parameters = self.sql(expression, "params_struct")
    3920        return self.func("PREDICT", model, table, parameters or None)
    3921
    3922    def forin_sql(self, expression: exp.ForIn) -> str:
    3923        this = self.sql(expression, "this")
    3924        expression_sql = self.sql(expression, "expression")
    3925        return f"FOR {this} DO {expression_sql}"
    3926
    3927    def refresh_sql(self, expression: exp.Refresh) -> str:
    3928        this = self.sql(expression, "this")
    3929        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3930        return f"REFRESH {table}{this}"
    3931
    3932    def toarray_sql(self, expression: exp.ToArray) -> str:
    3933        arg = expression.this
    3934        if not arg.type:
    3935            from sqlglot.optimizer.annotate_types import annotate_types
    3936
    3937            arg = annotate_types(arg)
    3938
    3939        if arg.is_type(exp.DataType.Type.ARRAY):
    3940            return self.sql(arg)
    3941
    3942        cond_for_null = arg.is_(exp.null())
    3943        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
    3944
    3945    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3946        this = expression.this
    3947        time_format = self.format_time(expression)
    3948
    3949        if time_format:
    3950            return self.sql(
    3951                exp.cast(
    3952                    exp.StrToTime(this=this, format=expression.args["format"]),
    3953                    exp.DataType.Type.TIME,
    3954                )
    3955            )
    3956
    3957        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3958            return self.sql(this)
    3959
    3960        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
    3961
    3962    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3963        this = expression.this
    3964        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3965            return self.sql(this)
    3966
    3967        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
    3968
    3969    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    3970        this = expression.this
    3971        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    3972            return self.sql(this)
    3973
    3974        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
    3975
    3976    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3977        this = expression.this
    3978        time_format = self.format_time(expression)
    3979
    3980        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3981            return self.sql(
    3982                exp.cast(
    3983                    exp.StrToTime(this=this, format=expression.args["format"]),
    3984                    exp.DataType.Type.DATE,
    3985                )
    3986            )
    3987
    3988        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3989            return self.sql(this)
    3990
    3991        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
    3992
    3993    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3994        return self.sql(
    3995            exp.func(
    3996                "DATEDIFF",
    3997                expression.this,
    3998                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    3999                "day",
    4000            )
    4001        )
    4002
    4003    def lastday_sql(self, expression: exp.LastDay) -> str:
    4004        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4005            return self.function_fallback_sql(expression)
    4006
    4007        unit = expression.text("unit")
    4008        if unit and unit != "MONTH":
    4009            self.unsupported("Date parts are not supported in LAST_DAY.")
    4010
    4011        return self.func("LAST_DAY", expression.this)
    4012
    4013    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4014        from sqlglot.dialects.dialect import unit_to_str
    4015
    4016        return self.func(
    4017            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4018        )
    4019
    4020    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4021        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4022            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4023            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4024            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4025            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4026
    4027        from sqlglot.dialects import Dialect
    4028
    4029        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4030        if self.dialect.__class__ != Dialect:
    4031            self.unsupported("ARRAY_ANY is unsupported")
    4032
    4033        return self.function_fallback_sql(expression)
    4034
    4035    def struct_sql(self, expression: exp.Struct) -> str:
    4036        expression.set(
    4037            "expressions",
    4038            [
    4039                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4040                if isinstance(e, exp.PropertyEQ)
    4041                else e
    4042                for e in expression.expressions
    4043            ],
    4044        )
    4045
    4046        return self.function_fallback_sql(expression)
    4047
    4048    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4049        low = self.sql(expression, "this")
    4050        high = self.sql(expression, "expression")
    4051
    4052        return f"{low} TO {high}"
    4053
    4054    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4055        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4056        tables = f" {self.expressions(expression)}"
    4057
    4058        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4059
    4060        on_cluster = self.sql(expression, "cluster")
    4061        on_cluster = f" {on_cluster}" if on_cluster else ""
    4062
    4063        identity = self.sql(expression, "identity")
    4064        identity = f" {identity} IDENTITY" if identity else ""
    4065
    4066        option = self.sql(expression, "option")
    4067        option = f" {option}" if option else ""
    4068
    4069        partition = self.sql(expression, "partition")
    4070        partition = f" {partition}" if partition else ""
    4071
    4072        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
    4073
    4074    # This transpiles T-SQL's CONVERT function
    4075    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
    4076    def convert_sql(self, expression: exp.Convert) -> str:
    4077        to = expression.this
    4078        value = expression.expression
    4079        style = expression.args.get("style")
    4080        safe = expression.args.get("safe")
    4081        strict = expression.args.get("strict")
    4082
    4083        if not to or not value:
    4084            return ""
    4085
    4086        # Retrieve length of datatype and override to default if not specified
    4087        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4088            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4089
    4090        transformed: t.Optional[exp.Expression] = None
    4091        cast = exp.Cast if strict else exp.TryCast
    4092
    4093        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4094        if isinstance(style, exp.Literal) and style.is_int:
    4095            from sqlglot.dialects.tsql import TSQL
    4096
    4097            style_value = style.name
    4098            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4099            if not converted_style:
    4100                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4101
    4102            fmt = exp.Literal.string(converted_style)
    4103
    4104            if to.this == exp.DataType.Type.DATE:
    4105                transformed = exp.StrToDate(this=value, format=fmt)
    4106            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4107                transformed = exp.StrToTime(this=value, format=fmt)
    4108            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4109                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4110            elif to.this == exp.DataType.Type.TEXT:
    4111                transformed = exp.TimeToStr(this=value, format=fmt)
    4112
    4113        if not transformed:
    4114            transformed = cast(this=value, to=to, safe=safe)
    4115
    4116        return self.sql(transformed)
    4117
    4118    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
    4119        this = expression.this
    4120        if isinstance(this, exp.JSONPathWildcard):
    4121            this = self.json_path_part(this)
    4122            return f".{this}" if this else ""
    4123
    4124        if exp.SAFE_IDENTIFIER_RE.match(this):
    4125            return f".{this}"
    4126
    4127        this = self.json_path_part(this)
    4128        return (
    4129            f"[{this}]"
    4130            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
    4131            else f".{this}"
    4132        )
    4133
    4134    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
    4135        this = self.json_path_part(expression.this)
    4136        return f"[{this}]" if this else ""
    4137
    4138    def _simplify_unless_literal(self, expression: E) -> E:
    4139        if not isinstance(expression, exp.Literal):
    4140            from sqlglot.optimizer.simplify import simplify
    4141
    4142            expression = simplify(expression, dialect=self.dialect)
    4143
    4144        return expression
    4145
    4146    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
    4147        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
    4148            # The first modifier here will be the one closest to the AggFunc's arg
    4149            mods = sorted(
    4150                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
    4151                key=lambda x: 0
    4152                if isinstance(x, exp.HavingMax)
    4153                else (1 if isinstance(x, exp.Order) else 2),
    4154            )
    4155
    4156            if mods:
    4157                mod = mods[0]
    4158                this = expression.__class__(this=mod.this.copy())
    4159                this.meta["inline"] = True
    4160                mod.this.replace(this)
    4161                return self.sql(expression.this)
    4162
    4163            agg_func = expression.find(exp.AggFunc)
    4164
    4165            if agg_func:
    4166                return self.sql(agg_func)[:-1] + f" {text})"
    4167
    4168        return f"{self.sql(expression, 'this')} {text}"
    4169
    4170    def _replace_line_breaks(self, string: str) -> str:
    4171        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
    4172        if self.pretty:
    4173            return string.replace("\n", self.SENTINEL_LINE_BREAK)
    4174        return string
    4175
    4176    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4177        option = self.sql(expression, "this")
    4178
    4179        if expression.expressions:
    4180            upper = option.upper()
    4181
    4182            # Snowflake FILE_FORMAT options are separated by whitespace
    4183            sep = " " if upper == "FILE_FORMAT" else ", "
    4184
    4185            # Databricks copy/format options do not set their list of values with EQ
    4186            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4187            values = self.expressions(expression, flat=True, sep=sep)
    4188            return f"{option}{op}({values})"
    4189
    4190        value = self.sql(expression, "expression")
    4191
    4192        if not value:
    4193            return option
    4194
    4195        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4196
    4197        return f"{option}{op}{value}"
    4198
    4199    def credentials_sql(self, expression: exp.Credentials) -> str:
    4200        cred_expr = expression.args.get("credentials")
    4201        if isinstance(cred_expr, exp.Literal):
    4202            # Redshift case: CREDENTIALS <string>
    4203            credentials = self.sql(expression, "credentials")
    4204            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4205        else:
    4206            # Snowflake case: CREDENTIALS = (...)
    4207            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4208            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4209
    4210        storage = self.sql(expression, "storage")
    4211        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4212
    4213        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4214        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4215
    4216        iam_role = self.sql(expression, "iam_role")
    4217        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4218
    4219        region = self.sql(expression, "region")
    4220        region = f" REGION {region}" if region else ""
    4221
    4222        return f"{credentials}{storage}{encryption}{iam_role}{region}"
    4223
    4224    def copy_sql(self, expression: exp.Copy) -> str:
    4225        this = self.sql(expression, "this")
    4226        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4227
    4228        credentials = self.sql(expression, "credentials")
    4229        credentials = self.seg(credentials) if credentials else ""
    4230        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    4231        files = self.expressions(expression, key="files", flat=True)
    4232
    4233        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4234        params = self.expressions(
    4235            expression,
    4236            key="params",
    4237            sep=sep,
    4238            new_line=True,
    4239            skip_last=True,
    4240            skip_first=True,
    4241            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4242        )
    4243
    4244        if params:
    4245            if self.COPY_PARAMS_ARE_WRAPPED:
    4246                params = f" WITH ({params})"
    4247            elif not self.pretty:
    4248                params = f" {params}"
    4249
    4250        return f"COPY{this}{kind} {files}{credentials}{params}"
    4251
    4252    def semicolon_sql(self, expression: exp.Semicolon) -> str:
    4253        return ""
    4254
    4255    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4256        on_sql = "ON" if expression.args.get("on") else "OFF"
    4257        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4258        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4259        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4260        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4261
    4262        if filter_col or retention_period:
    4263            on_sql = self.func("ON", filter_col, retention_period)
    4264
    4265        return f"DATA_DELETION={on_sql}"
    4266
    4267    def maskingpolicycolumnconstraint_sql(
    4268        self, expression: exp.MaskingPolicyColumnConstraint
    4269    ) -> str:
    4270        this = self.sql(expression, "this")
    4271        expressions = self.expressions(expression, flat=True)
    4272        expressions = f" USING ({expressions})" if expressions else ""
    4273        return f"MASKING POLICY {this}{expressions}"
    4274
    4275    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4276        this = self.sql(expression, "this")
    4277        this = f"TABLE {this}"
    4278        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
    4279
    4280    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    4281        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
    4282
    4283    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4284        this = self.sql(expression, "this")
    4285        expr = expression.expression
    4286
    4287        if isinstance(expr, exp.Func):
    4288            # T-SQL's CLR functions are case sensitive
    4289            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4290        else:
    4291            expr = self.sql(expression, "expression")
    4292
    4293        return self.scope_resolution(expr, this)
    4294
    4295    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4296        if self.PARSE_JSON_NAME is None:
    4297            return self.sql(expression.this)
    4298
    4299        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
    4300
    4301    def rand_sql(self, expression: exp.Rand) -> str:
    4302        lower = self.sql(expression, "lower")
    4303        upper = self.sql(expression, "upper")
    4304
    4305        if lower and upper:
    4306            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4307        return self.func("RAND", expression.this)
    4308
    4309    def changes_sql(self, expression: exp.Changes) -> str:
    4310        information = self.sql(expression, "information")
    4311        information = f"INFORMATION => {information}"
    4312        at_before = self.sql(expression, "at_before")
    4313        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4314        end = self.sql(expression, "end")
    4315        end = f"{self.seg('')}{end}" if end else ""
    4316
    4317        return f"CHANGES ({information}){at_before}{end}"
    4318
    4319    def pad_sql(self, expression: exp.Pad) -> str:
    4320        prefix = "L" if expression.args.get("is_left") else "R"
    4321
    4322        fill_pattern = self.sql(expression, "fill_pattern") or None
    4323        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4324            fill_pattern = "' '"
    4325
    4326        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
    4327
    4328    def summarize_sql(self, expression: exp.Summarize) -> str:
    4329        table = " TABLE" if expression.args.get("table") else ""
    4330        return f"SUMMARIZE{table} {self.sql(expression.this)}"
    4331
    4332    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4333        generate_series = exp.GenerateSeries(**expression.args)
    4334
    4335        parent = expression.parent
    4336        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4337            parent = parent.parent
    4338
    4339        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4340            return self.sql(exp.Unnest(expressions=[generate_series]))
    4341
    4342        if isinstance(parent, exp.Select):
    4343            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4344
    4345        return self.sql(generate_series)
    4346
    4347    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4348        exprs = expression.expressions
    4349        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4350            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4351        else:
    4352            rhs = self.expressions(expression)
    4353
    4354        return self.func(name, expression.this, rhs or None)
    4355
    4356    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4357        if self.SUPPORTS_CONVERT_TIMEZONE:
    4358            return self.function_fallback_sql(expression)
    4359
    4360        source_tz = expression.args.get("source_tz")
    4361        target_tz = expression.args.get("target_tz")
    4362        timestamp = expression.args.get("timestamp")
    4363
    4364        if source_tz and timestamp:
    4365            timestamp = exp.AtTimeZone(
    4366                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4367            )
    4368
    4369        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4370
    4371        return self.sql(expr)
    4372
    4373    def json_sql(self, expression: exp.JSON) -> str:
    4374        this = self.sql(expression, "this")
    4375        this = f" {this}" if this else ""
    4376
    4377        _with = expression.args.get("with")
    4378
    4379        if _with is None:
    4380            with_sql = ""
    4381        elif not _with:
    4382            with_sql = " WITHOUT"
    4383        else:
    4384            with_sql = " WITH"
    4385
    4386        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4387
    4388        return f"JSON{this}{with_sql}{unique_sql}"
    4389
    4390    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4391        def _generate_on_options(arg: t.Any) -> str:
    4392            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4393
    4394        path = self.sql(expression, "path")
    4395        returning = self.sql(expression, "returning")
    4396        returning = f" RETURNING {returning}" if returning else ""
    4397
    4398        on_condition = self.sql(expression, "on_condition")
    4399        on_condition = f" {on_condition}" if on_condition else ""
    4400
    4401        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
    4402
    4403    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4404        else_ = "ELSE " if expression.args.get("else_") else ""
    4405        condition = self.sql(expression, "expression")
    4406        condition = f"WHEN {condition} THEN " if condition else else_
    4407        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4408        return f"{condition}{insert}"
    4409
    4410    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4411        kind = self.sql(expression, "kind")
    4412        expressions = self.seg(self.expressions(expression, sep=" "))
    4413        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4414        return res
    4415
    4416    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4417        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4418        empty = expression.args.get("empty")
    4419        empty = (
    4420            f"DEFAULT {empty} ON EMPTY"
    4421            if isinstance(empty, exp.Expression)
    4422            else self.sql(expression, "empty")
    4423        )
    4424
    4425        error = expression.args.get("error")
    4426        error = (
    4427            f"DEFAULT {error} ON ERROR"
    4428            if isinstance(error, exp.Expression)
    4429            else self.sql(expression, "error")
    4430        )
    4431
    4432        if error and empty:
    4433            error = (
    4434                f"{empty} {error}"
    4435                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4436                else f"{error} {empty}"
    4437            )
    4438            empty = ""
    4439
    4440        null = self.sql(expression, "null")
    4441
    4442        return f"{empty}{error}{null}"
    4443
    4444    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
    4445        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
    4446        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
    4447
    4448    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4449        this = self.sql(expression, "this")
    4450        path = self.sql(expression, "path")
    4451
    4452        passing = self.expressions(expression, "passing")
    4453        passing = f" PASSING {passing}" if passing else ""
    4454
    4455        on_condition = self.sql(expression, "on_condition")
    4456        on_condition = f" {on_condition}" if on_condition else ""
    4457
    4458        path = f"{path}{passing}{on_condition}"
    4459
    4460        return self.func("JSON_EXISTS", this, path)
    4461
    4462    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4463        array_agg = self.function_fallback_sql(expression)
    4464
    4465        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4466        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4467        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4468            parent = expression.parent
    4469            if isinstance(parent, exp.Filter):
    4470                parent_cond = parent.expression.this
    4471                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4472            else:
    4473                this = expression.this
    4474                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4475                if this.find(exp.Column):
    4476                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4477                    this_sql = (
    4478                        self.expressions(this)
    4479                        if isinstance(this, exp.Distinct)
    4480                        else self.sql(expression, "this")
    4481                    )
    4482
    4483                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4484
    4485        return array_agg
    4486
    4487    def apply_sql(self, expression: exp.Apply) -> str:
    4488        this = self.sql(expression, "this")
    4489        expr = self.sql(expression, "expression")
    4490
    4491        return f"{this} APPLY({expr})"
    4492
    4493    def grant_sql(self, expression: exp.Grant) -> str:
    4494        privileges_sql = self.expressions(expression, key="privileges", flat=True)
    4495
    4496        kind = self.sql(expression, "kind")
    4497        kind = f" {kind}" if kind else ""
    4498
    4499        securable = self.sql(expression, "securable")
    4500        securable = f" {securable}" if securable else ""
    4501
    4502        principals = self.expressions(expression, key="principals", flat=True)
    4503
    4504        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
    4505
    4506        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
    4507
    4508    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4509        this = self.sql(expression, "this")
    4510        columns = self.expressions(expression, flat=True)
    4511        columns = f"({columns})" if columns else ""
    4512
    4513        return f"{this}{columns}"
    4514
    4515    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4516        this = self.sql(expression, "this")
    4517
    4518        kind = self.sql(expression, "kind")
    4519        kind = f"{kind} " if kind else ""
    4520
    4521        return f"{kind}{this}"
    4522
    4523    def columns_sql(self, expression: exp.Columns):
    4524        func = self.function_fallback_sql(expression)
    4525        if expression.args.get("unpack"):
    4526            func = f"*{func}"
    4527
    4528        return func
    4529
    4530    def overlay_sql(self, expression: exp.Overlay):
    4531        this = self.sql(expression, "this")
    4532        expr = self.sql(expression, "expression")
    4533        from_sql = self.sql(expression, "from")
    4534        for_sql = self.sql(expression, "for")
    4535        for_sql = f" FOR {for_sql}" if for_sql else ""
    4536
    4537        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
    4538
    4539    @unsupported_args("format")
    4540    def todouble_sql(self, expression: exp.ToDouble) -> str:
    4541        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    4542
    4543    def string_sql(self, expression: exp.String) -> str:
    4544        this = expression.this
    4545        zone = expression.args.get("zone")
    4546
    4547        if zone:
    4548            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4549            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4550            # set for source_tz to transpile the time conversion before the STRING cast
    4551            this = exp.ConvertTimezone(
    4552                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4553            )
    4554
    4555        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
    4556
    4557    def median_sql(self, expression: exp.Median):
    4558        if not self.SUPPORTS_MEDIAN:
    4559            return self.sql(
    4560                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4561            )
    4562
    4563        return self.function_fallback_sql(expression)
    4564
    4565    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4566        filler = self.sql(expression, "this")
    4567        filler = f" {filler}" if filler else ""
    4568        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4569        return f"TRUNCATE{filler} {with_count}"
    4570
    4571    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4572        if self.SUPPORTS_UNIX_SECONDS:
    4573            return self.function_fallback_sql(expression)
    4574
    4575        start_ts = exp.cast(
    4576            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4577        )
    4578
    4579        return self.sql(
    4580            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    4581        )
    4582
    4583    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    4584        dim = expression.expression
    4585
    4586        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    4587        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    4588            if not (dim.is_int and dim.name == "1"):
    4589                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    4590            dim = None
    4591
    4592        # If dimension is required but not specified, default initialize it
    4593        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    4594            dim = exp.Literal.number(1)
    4595
    4596        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
    4597
    4598    def attach_sql(self, expression: exp.Attach) -> str:
    4599        this = self.sql(expression, "this")
    4600        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    4601        expressions = self.expressions(expression)
    4602        expressions = f" ({expressions})" if expressions else ""
    4603
    4604        return f"ATTACH{exists_sql} {this}{expressions}"
    4605
    4606    def detach_sql(self, expression: exp.Detach) -> str:
    4607        this = self.sql(expression, "this")
    4608        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    4609
    4610        return f"DETACH{exists_sql} {this}"
    4611
    4612    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    4613        this = self.sql(expression, "this")
    4614        value = self.sql(expression, "expression")
    4615        value = f" {value}" if value else ""
    4616        return f"{this}{value}"
    4617
    4618    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4619        this_sql = self.sql(expression, "this")
    4620        if isinstance(expression.this, exp.Table):
    4621            this_sql = f"TABLE {this_sql}"
    4622
    4623        return self.func(
    4624            "FEATURES_AT_TIME",
    4625            this_sql,
    4626            expression.args.get("time"),
    4627            expression.args.get("num_rows"),
    4628            expression.args.get("ignore_feature_nulls"),
    4629        )
    4630
    4631    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    4632        return (
    4633            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    4634        )
    4635
    4636    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    4637        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    4638        encode = f"{encode} {self.sql(expression, 'this')}"
    4639
    4640        properties = expression.args.get("properties")
    4641        if properties:
    4642            encode = f"{encode} {self.properties(properties)}"
    4643
    4644        return encode
    4645
    4646    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    4647        this = self.sql(expression, "this")
    4648        include = f"INCLUDE {this}"
    4649
    4650        column_def = self.sql(expression, "column_def")
    4651        if column_def:
    4652            include = f"{include} {column_def}"
    4653
    4654        alias = self.sql(expression, "alias")
    4655        if alias:
    4656            include = f"{include} AS {alias}"
    4657
    4658        return include
    4659
    4660    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
    4661        name = f"NAME {self.sql(expression, 'this')}"
    4662        return self.func("XMLELEMENT", name, *expression.expressions)
    4663
    4664    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    4665        partitions = self.expressions(expression, "partition_expressions")
    4666        create = self.expressions(expression, "create_expressions")
    4667        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
    4668
    4669    def partitionbyrangepropertydynamic_sql(
    4670        self, expression: exp.PartitionByRangePropertyDynamic
    4671    ) -> str:
    4672        start = self.sql(expression, "start")
    4673        end = self.sql(expression, "end")
    4674
    4675        every = expression.args["every"]
    4676        if isinstance(every, exp.Interval) and every.this.is_string:
    4677            every.this.replace(exp.Literal.number(every.name))
    4678
    4679        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
    4680
    4681    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    4682        name = self.sql(expression, "this")
    4683        values = self.expressions(expression, flat=True)
    4684
    4685        return f"NAME {name} VALUE {values}"
    4686
    4687    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    4688        kind = self.sql(expression, "kind")
    4689        sample = self.sql(expression, "sample")
    4690        return f"SAMPLE {sample} {kind}"
    4691
    4692    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    4693        kind = self.sql(expression, "kind")
    4694        option = self.sql(expression, "option")
    4695        option = f" {option}" if option else ""
    4696        this = self.sql(expression, "this")
    4697        this = f" {this}" if this else ""
    4698        columns = self.expressions(expression)
    4699        columns = f" {columns}" if columns else ""
    4700        return f"{kind}{option} STATISTICS{this}{columns}"
    4701
    4702    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    4703        this = self.sql(expression, "this")
    4704        columns = self.expressions(expression)
    4705        inner_expression = self.sql(expression, "expression")
    4706        inner_expression = f" {inner_expression}" if inner_expression else ""
    4707        update_options = self.sql(expression, "update_options")
    4708        update_options = f" {update_options} UPDATE" if update_options else ""
    4709        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
    4710
    4711    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    4712        kind = self.sql(expression, "kind")
    4713        kind = f" {kind}" if kind else ""
    4714        return f"DELETE{kind} STATISTICS"
    4715
    4716    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
    4717        inner_expression = self.sql(expression, "expression")
    4718        return f"LIST CHAINED ROWS{inner_expression}"
    4719
    4720    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    4721        kind = self.sql(expression, "kind")
    4722        this = self.sql(expression, "this")
    4723        this = f" {this}" if this else ""
    4724        inner_expression = self.sql(expression, "expression")
    4725        return f"VALIDATE {kind}{this}{inner_expression}"
    4726
    4727    def analyze_sql(self, expression: exp.Analyze) -> str:
    4728        options = self.expressions(expression, key="options", sep=" ")
    4729        options = f" {options}" if options else ""
    4730        kind = self.sql(expression, "kind")
    4731        kind = f" {kind}" if kind else ""
    4732        this = self.sql(expression, "this")
    4733        this = f" {this}" if this else ""
    4734        mode = self.sql(expression, "mode")
    4735        mode = f" {mode}" if mode else ""
    4736        properties = self.sql(expression, "properties")
    4737        properties = f" {properties}" if properties else ""
    4738        partition = self.sql(expression, "partition")
    4739        partition = f" {partition}" if partition else ""
    4740        inner_expression = self.sql(expression, "expression")
    4741        inner_expression = f" {inner_expression}" if inner_expression else ""
    4742        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
    4743
    4744    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    4745        this = self.sql(expression, "this")
    4746        namespaces = self.expressions(expression, key="namespaces")
    4747        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    4748        passing = self.expressions(expression, key="passing")
    4749        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    4750        columns = self.expressions(expression, key="columns")
    4751        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    4752        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    4753        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
    4754
    4755    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
    4756        this = self.sql(expression, "this")
    4757        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
    4758
    4759    def export_sql(self, expression: exp.Export) -> str:
    4760        this = self.sql(expression, "this")
    4761        connection = self.sql(expression, "connection")
    4762        connection = f"WITH CONNECTION {connection} " if connection else ""
    4763        options = self.sql(expression, "options")
    4764        return f"EXPORT DATA {connection}{options} AS {this}"

logger = <Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE = re.compile('\\\\(\\d+)')
UNSUPPORTED_TEMPLATE = "Argument '{}' is not supported for expression '{}' when targeting {}."
def unsupported_args( *args: Union[str, Tuple[str, str]]) -> Callable[[Callable[[~G, ~E], str]], Callable[[~G, ~E], str]]: View Source

    30def unsupported_args(
    31    *args: t.Union[str, t.Tuple[str, str]],
    32) -> t.Callable[[GeneratorMethod], GeneratorMethod]:
    33    """
    34    Decorator that can be used to mark certain args of an `Expression` subclass as unsupported.
    35    It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).
    36    """
    37    diagnostic_by_arg: t.Dict[str, t.Optional[str]] = {}
    38    for arg in args:
    39        if isinstance(arg, str):
    40            diagnostic_by_arg[arg] = None
    41        else:
    42            diagnostic_by_arg[arg[0]] = arg[1]
    43
    44    def decorator(func: GeneratorMethod) -> GeneratorMethod:
    45        @wraps(func)
    46        def _func(generator: G, expression: E) -> str:
    47            expression_name = expression.__class__.__name__
    48            dialect_name = generator.dialect.__class__.__name__
    49
    50            for arg_name, diagnostic in diagnostic_by_arg.items():
    51                if expression.args.get(arg_name):
    52                    diagnostic = diagnostic or UNSUPPORTED_TEMPLATE.format(
    53                        arg_name, expression_name, dialect_name
    54                    )
    55                    generator.unsupported(diagnostic)
    56
    57            return func(generator, expression)
    58
    59        return _func
    60
    61    return decorator

Decorator that can be used to mark certain args of an `Expression` subclass as unsupported. It expects a sequence of argument names or pairs of the form (argument_name, diagnostic_msg).

class Generator: View Source

      75class Generator(metaclass=_Generator):
      76    """
      77    Generator converts a given syntax tree to the corresponding SQL string.
      78
      79    Args:
      80        pretty: Whether to format the produced SQL string.
      81            Default: False.
      82        identify: Determines when an identifier should be quoted. Possible values are:
      83            False (default): Never quote, except in cases where it's mandatory by the dialect.
      84            True or 'always': Always quote.
      85            'safe': Only quote identifiers that are case insensitive.
      86        normalize: Whether to normalize identifiers to lowercase.
      87            Default: False.
      88        pad: The pad size in a formatted string. For example, this affects the indentation of
      89            a projection in a query, relative to its nesting level.
      90            Default: 2.
      91        indent: The indentation size in a formatted string. For example, this affects the
      92            indentation of subqueries and filters under a `WHERE` clause.
      93            Default: 2.
      94        normalize_functions: How to normalize function names. Possible values are:
      95            "upper" or True (default): Convert names to uppercase.
      96            "lower": Convert names to lowercase.
      97            False: Disables function name normalization.
      98        unsupported_level: Determines the generator's behavior when it encounters unsupported expressions.
      99            Default ErrorLevel.WARN.
     100        max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError.
     101            This is only relevant if unsupported_level is ErrorLevel.RAISE.
     102            Default: 3
     103        leading_comma: Whether the comma is leading or trailing in select expressions.
     104            This is only relevant when generating in pretty mode.
     105            Default: False
     106        max_text_width: The max number of characters in a segment before creating new lines in pretty mode.
     107            The default is on the smaller end because the length only represents a segment and not the true
     108            line length.
     109            Default: 80
     110        comments: Whether to preserve comments in the output SQL code.
     111            Default: True
     112    """
     113
     114    TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = {
     115        **JSON_PATH_PART_TRANSFORMS,
     116        exp.AllowedValuesProperty: lambda self,
     117        e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}",
     118        exp.AnalyzeColumns: lambda self, e: self.sql(e, "this"),
     119        exp.AnalyzeWith: lambda self, e: self.expressions(e, prefix="WITH ", sep=" "),
     120        exp.ArrayContainsAll: lambda self, e: self.binary(e, "@>"),
     121        exp.ArrayOverlaps: lambda self, e: self.binary(e, "&&"),
     122        exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}",
     123        exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}",
     124        exp.CaseSpecificColumnConstraint: lambda _,
     125        e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC",
     126        exp.Ceil: lambda self, e: self.ceil_floor(e),
     127        exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}",
     128        exp.CharacterSetProperty: lambda self,
     129        e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}",
     130        exp.ClusteredColumnConstraint: lambda self,
     131        e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})",
     132        exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}",
     133        exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}",
     134        exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}",
     135        exp.CopyGrantsProperty: lambda *_: "COPY GRANTS",
     136        exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}",
     137        exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}",
     138        exp.DynamicProperty: lambda *_: "DYNAMIC",
     139        exp.EmptyProperty: lambda *_: "EMPTY",
     140        exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}",
     141        exp.EphemeralColumnConstraint: lambda self,
     142        e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}",
     143        exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}",
     144        exp.ExecuteAsProperty: lambda self, e: self.naked_property(e),
     145        exp.Except: lambda self, e: self.set_operations(e),
     146        exp.ExternalProperty: lambda *_: "EXTERNAL",
     147        exp.Floor: lambda self, e: self.ceil_floor(e),
     148        exp.GlobalProperty: lambda *_: "GLOBAL",
     149        exp.HeapProperty: lambda *_: "HEAP",
     150        exp.IcebergProperty: lambda *_: "ICEBERG",
     151        exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})",
     152        exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}",
     153        exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}",
     154        exp.Intersect: lambda self, e: self.set_operations(e),
     155        exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}",
     156        exp.Int64: lambda self, e: self.sql(exp.cast(e.this, exp.DataType.Type.BIGINT)),
     157        exp.LanguageProperty: lambda self, e: self.naked_property(e),
     158        exp.LocationProperty: lambda self, e: self.naked_property(e),
     159        exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG",
     160        exp.MaterializedProperty: lambda *_: "MATERIALIZED",
     161        exp.NonClusteredColumnConstraint: lambda self,
     162        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
     163        exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX",
     164        exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION",
     165        exp.OnCommitProperty: lambda _,
     166        e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS",
     167        exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}",
     168        exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}",
     169        exp.Operator: lambda self, e: self.binary(e, ""),  # The operator is produced in `binary`
     170        exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}",
     171        exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}",
     172        exp.PivotAny: lambda self, e: f"ANY{self.sql(e, 'this')}",
     173        exp.ProjectionPolicyColumnConstraint: lambda self,
     174        e: f"PROJECTION POLICY {self.sql(e, 'this')}",
     175        exp.RemoteWithConnectionModelProperty: lambda self,
     176        e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}",
     177        exp.ReturnsProperty: lambda self, e: (
     178            "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e)
     179        ),
     180        exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}",
     181        exp.SecureProperty: lambda *_: "SECURE",
     182        exp.SecurityProperty: lambda self, e: f"SECURITY {self.sql(e, 'this')}",
     183        exp.SetConfigProperty: lambda self, e: self.sql(e, "this"),
     184        exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET",
     185        exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}",
     186        exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}",
     187        exp.SqlReadWriteProperty: lambda _, e: e.name,
     188        exp.SqlSecurityProperty: lambda _,
     189        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
     190        exp.StabilityProperty: lambda _, e: e.name,
     191        exp.Stream: lambda self, e: f"STREAM {self.sql(e, 'this')}",
     192        exp.StreamingTableProperty: lambda *_: "STREAMING",
     193        exp.StrictProperty: lambda *_: "STRICT",
     194        exp.SwapTable: lambda self, e: f"SWAP WITH {self.sql(e, 'this')}",
     195        exp.TemporaryProperty: lambda *_: "TEMPORARY",
     196        exp.Tags: lambda self, e: f"TAG ({self.expressions(e, flat=True)})",
     197        exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}",
     198        exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}",
     199        exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}",
     200        exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions),
     201        exp.TransientProperty: lambda *_: "TRANSIENT",
     202        exp.Union: lambda self, e: self.set_operations(e),
     203        exp.UnloggedProperty: lambda *_: "UNLOGGED",
     204        exp.UsingData: lambda self, e: f"USING DATA {self.sql(e, 'this')}",
     205        exp.Uuid: lambda *_: "UUID()",
     206        exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE",
     207        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
     208        exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}",
     209        exp.VolatileProperty: lambda *_: "VOLATILE",
     210        exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}",
     211        exp.WithProcedureOptions: lambda self, e: f"WITH {self.expressions(e, flat=True)}",
     212        exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}",
     213        exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}",
     214    }
     215
     216    # Whether null ordering is supported in order by
     217    # True: Full Support, None: No support, False: No support for certain cases
     218    # such as window specifications, aggregate functions etc
     219    NULL_ORDERING_SUPPORTED: t.Optional[bool] = True
     220
     221    # Whether ignore nulls is inside the agg or outside.
     222    # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER
     223    IGNORE_NULLS_IN_FUNC = False
     224
     225    # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported
     226    LOCKING_READS_SUPPORTED = False
     227
     228    # Whether the EXCEPT and INTERSECT operations can return duplicates
     229    EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
     230
     231    # Wrap derived values in parens, usually standard but spark doesn't support it
     232    WRAP_DERIVED_VALUES = True
     233
     234    # Whether create function uses an AS before the RETURN
     235    CREATE_FUNCTION_RETURN_AS = True
     236
     237    # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
     238    MATCHED_BY_SOURCE = True
     239
     240    # Whether the INTERVAL expression works only with values like '1 day'
     241    SINGLE_STRING_INTERVAL = False
     242
     243    # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs
     244    INTERVAL_ALLOWS_PLURAL_FORM = True
     245
     246    # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH")
     247    LIMIT_FETCH = "ALL"
     248
     249    # Whether limit and fetch allows expresions or just limits
     250    LIMIT_ONLY_LITERALS = False
     251
     252    # Whether a table is allowed to be renamed with a db
     253    RENAME_TABLE_WITH_DB = True
     254
     255    # The separator for grouping sets and rollups
     256    GROUPINGS_SEP = ","
     257
     258    # The string used for creating an index on a table
     259    INDEX_ON = "ON"
     260
     261    # Whether join hints should be generated
     262    JOIN_HINTS = True
     263
     264    # Whether table hints should be generated
     265    TABLE_HINTS = True
     266
     267    # Whether query hints should be generated
     268    QUERY_HINTS = True
     269
     270    # What kind of separator to use for query hints
     271    QUERY_HINT_SEP = ", "
     272
     273    # Whether comparing against booleans (e.g. x IS TRUE) is supported
     274    IS_BOOL_ALLOWED = True
     275
     276    # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement
     277    DUPLICATE_KEY_UPDATE_WITH_SET = True
     278
     279    # Whether to generate the limit as TOP <value> instead of LIMIT <value>
     280    LIMIT_IS_TOP = False
     281
     282    # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
     283    RETURNING_END = True
     284
     285    # Whether to generate an unquoted value for EXTRACT's date part argument
     286    EXTRACT_ALLOWS_QUOTES = True
     287
     288    # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax
     289    TZ_TO_WITH_TIME_ZONE = False
     290
     291    # Whether the NVL2 function is supported
     292    NVL2_SUPPORTED = True
     293
     294    # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
     295    SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
     296
     297    # Whether VALUES statements can be used as derived tables.
     298    # MySQL 5 and Redshift do not allow this, so when False, it will convert
     299    # SELECT * VALUES into SELECT UNION
     300    VALUES_AS_TABLE = True
     301
     302    # Whether the word COLUMN is included when adding a column with ALTER TABLE
     303    ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
     304
     305    # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     306    UNNEST_WITH_ORDINALITY = True
     307
     308    # Whether FILTER (WHERE cond) can be used for conditional aggregation
     309    AGGREGATE_FILTER_SUPPORTED = True
     310
     311    # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds
     312    SEMI_ANTI_JOIN_WITH_SIDE = True
     313
     314    # Whether to include the type of a computed column in the CREATE DDL
     315    COMPUTED_COLUMN_WITH_TYPE = True
     316
     317    # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY
     318    SUPPORTS_TABLE_COPY = True
     319
     320    # Whether parentheses are required around the table sample's expression
     321    TABLESAMPLE_REQUIRES_PARENS = True
     322
     323    # Whether a table sample clause's size needs to be followed by the ROWS keyword
     324    TABLESAMPLE_SIZE_IS_ROWS = True
     325
     326    # The keyword(s) to use when generating a sample clause
     327    TABLESAMPLE_KEYWORDS = "TABLESAMPLE"
     328
     329    # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
     330    TABLESAMPLE_WITH_METHOD = True
     331
     332    # The keyword to use when specifying the seed of a sample clause
     333    TABLESAMPLE_SEED_KEYWORD = "SEED"
     334
     335    # Whether COLLATE is a function instead of a binary operator
     336    COLLATE_IS_FUNC = False
     337
     338    # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle)
     339    DATA_TYPE_SPECIFIERS_ALLOWED = False
     340
     341    # Whether conditions require booleans WHERE x = 0 vs WHERE x
     342    ENSURE_BOOLS = False
     343
     344    # Whether the "RECURSIVE" keyword is required when defining recursive CTEs
     345    CTE_RECURSIVE_KEYWORD_REQUIRED = True
     346
     347    # Whether CONCAT requires >1 arguments
     348    SUPPORTS_SINGLE_ARG_CONCAT = True
     349
     350    # Whether LAST_DAY function supports a date part argument
     351    LAST_DAY_SUPPORTS_DATE_PART = True
     352
     353    # Whether named columns are allowed in table aliases
     354    SUPPORTS_TABLE_ALIAS_COLUMNS = True
     355
     356    # Whether UNPIVOT aliases are Identifiers (False means they're Literals)
     357    UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
     358
     359    # What delimiter to use for separating JSON key/value pairs
     360    JSON_KEY_VALUE_PAIR_SEP = ":"
     361
     362    # INSERT OVERWRITE TABLE x override
     363    INSERT_OVERWRITE = " OVERWRITE TABLE"
     364
     365    # Whether the SELECT .. INTO syntax is used instead of CTAS
     366    SUPPORTS_SELECT_INTO = False
     367
     368    # Whether UNLOGGED tables can be created
     369    SUPPORTS_UNLOGGED_TABLES = False
     370
     371    # Whether the CREATE TABLE LIKE statement is supported
     372    SUPPORTS_CREATE_TABLE_LIKE = True
     373
     374    # Whether the LikeProperty needs to be specified inside of the schema clause
     375    LIKE_PROPERTY_INSIDE_SCHEMA = False
     376
     377    # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be
     378    # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args
     379    MULTI_ARG_DISTINCT = True
     380
     381    # Whether the JSON extraction operators expect a value of type JSON
     382    JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
     383
     384    # Whether bracketed keys like ["foo"] are supported in JSON paths
     385    JSON_PATH_BRACKETED_KEY_SUPPORTED = True
     386
     387    # Whether to escape keys using single quotes in JSON paths
     388    JSON_PATH_SINGLE_QUOTE_ESCAPE = False
     389
     390    # The JSONPathPart expressions supported by this dialect
     391    SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy()
     392
     393    # Whether any(f(x) for x in array) can be implemented by this dialect
     394    CAN_IMPLEMENT_ARRAY_ANY = False
     395
     396    # Whether the function TO_NUMBER is supported
     397    SUPPORTS_TO_NUMBER = True
     398
     399    # Whether or not set op modifiers apply to the outer set op or select.
     400    # SELECT * FROM x UNION SELECT * FROM y LIMIT 1
     401    # True means limit 1 happens after the set op, False means it it happens on y.
     402    SET_OP_MODIFIERS = True
     403
     404    # Whether parameters from COPY statement are wrapped in parentheses
     405    COPY_PARAMS_ARE_WRAPPED = True
     406
     407    # Whether values of params are set with "=" token or empty space
     408    COPY_PARAMS_EQ_REQUIRED = False
     409
     410    # Whether COPY statement has INTO keyword
     411    COPY_HAS_INTO_KEYWORD = True
     412
     413    # Whether the conditional TRY(expression) function is supported
     414    TRY_SUPPORTED = True
     415
     416    # Whether the UESCAPE syntax in unicode strings is supported
     417    SUPPORTS_UESCAPE = True
     418
     419    # The keyword to use when generating a star projection with excluded columns
     420    STAR_EXCEPT = "EXCEPT"
     421
     422    # The HEX function name
     423    HEX_FUNC = "HEX"
     424
     425    # The keywords to use when prefixing & separating WITH based properties
     426    WITH_PROPERTIES_PREFIX = "WITH"
     427
     428    # Whether to quote the generated expression of exp.JsonPath
     429    QUOTE_JSON_PATH = True
     430
     431    # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space)
     432    PAD_FILL_PATTERN_IS_REQUIRED = False
     433
     434    # Whether a projection can explode into multiple rows, e.g. by unnesting an array.
     435    SUPPORTS_EXPLODING_PROJECTIONS = True
     436
     437    # Whether ARRAY_CONCAT can be generated with varlen args or if it should be reduced to 2-arg version
     438    ARRAY_CONCAT_IS_VAR_LEN = True
     439
     440    # Whether CONVERT_TIMEZONE() is supported; if not, it will be generated as exp.AtTimeZone
     441    SUPPORTS_CONVERT_TIMEZONE = False
     442
     443    # Whether MEDIAN(expr) is supported; if not, it will be generated as PERCENTILE_CONT(expr, 0.5)
     444    SUPPORTS_MEDIAN = True
     445
     446    # Whether UNIX_SECONDS(timestamp) is supported
     447    SUPPORTS_UNIX_SECONDS = False
     448
     449    # The name to generate for the JSONPath expression. If `None`, only `this` will be generated
     450    PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON"
     451
     452    # The function name of the exp.ArraySize expression
     453    ARRAY_SIZE_NAME: str = "ARRAY_LENGTH"
     454
     455    # The syntax to use when altering the type of a column
     456    ALTER_SET_TYPE = "SET DATA TYPE"
     457
     458    # Whether exp.ArraySize should generate the dimension arg too (valid for Postgres & DuckDB)
     459    # None -> Doesn't support it at all
     460    # False (DuckDB) -> Has backwards-compatible support, but preferably generated without
     461    # True (Postgres) -> Explicitly requires it
     462    ARRAY_SIZE_DIM_REQUIRED: t.Optional[bool] = None
     463
     464    TYPE_MAPPING = {
     465        exp.DataType.Type.DATETIME2: "TIMESTAMP",
     466        exp.DataType.Type.NCHAR: "CHAR",
     467        exp.DataType.Type.NVARCHAR: "VARCHAR",
     468        exp.DataType.Type.MEDIUMTEXT: "TEXT",
     469        exp.DataType.Type.LONGTEXT: "TEXT",
     470        exp.DataType.Type.TINYTEXT: "TEXT",
     471        exp.DataType.Type.MEDIUMBLOB: "BLOB",
     472        exp.DataType.Type.LONGBLOB: "BLOB",
     473        exp.DataType.Type.TINYBLOB: "BLOB",
     474        exp.DataType.Type.INET: "INET",
     475        exp.DataType.Type.ROWVERSION: "VARBINARY",
     476        exp.DataType.Type.SMALLDATETIME: "TIMESTAMP",
     477    }
     478
     479    TIME_PART_SINGULARS = {
     480        "MICROSECONDS": "MICROSECOND",
     481        "SECONDS": "SECOND",
     482        "MINUTES": "MINUTE",
     483        "HOURS": "HOUR",
     484        "DAYS": "DAY",
     485        "WEEKS": "WEEK",
     486        "MONTHS": "MONTH",
     487        "QUARTERS": "QUARTER",
     488        "YEARS": "YEAR",
     489    }
     490
     491    AFTER_HAVING_MODIFIER_TRANSFORMS = {
     492        "cluster": lambda self, e: self.sql(e, "cluster"),
     493        "distribute": lambda self, e: self.sql(e, "distribute"),
     494        "sort": lambda self, e: self.sql(e, "sort"),
     495        "windows": lambda self, e: (
     496            self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True)
     497            if e.args.get("windows")
     498            else ""
     499        ),
     500        "qualify": lambda self, e: self.sql(e, "qualify"),
     501    }
     502
     503    TOKEN_MAPPING: t.Dict[TokenType, str] = {}
     504
     505    STRUCT_DELIMITER = ("<", ">")
     506
     507    PARAMETER_TOKEN = "@"
     508    NAMED_PLACEHOLDER_TOKEN = ":"
     509
     510    EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: t.Set[str] = set()
     511
     512    PROPERTIES_LOCATION = {
     513        exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA,
     514        exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE,
     515        exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA,
     516        exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA,
     517        exp.BackupProperty: exp.Properties.Location.POST_SCHEMA,
     518        exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME,
     519        exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA,
     520        exp.ChecksumProperty: exp.Properties.Location.POST_NAME,
     521        exp.CollateProperty: exp.Properties.Location.POST_SCHEMA,
     522        exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA,
     523        exp.Cluster: exp.Properties.Location.POST_SCHEMA,
     524        exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA,
     525        exp.DistributedByProperty: exp.Properties.Location.POST_SCHEMA,
     526        exp.DuplicateKeyProperty: exp.Properties.Location.POST_SCHEMA,
     527        exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME,
     528        exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA,
     529        exp.DefinerProperty: exp.Properties.Location.POST_CREATE,
     530        exp.DictRange: exp.Properties.Location.POST_SCHEMA,
     531        exp.DictProperty: exp.Properties.Location.POST_SCHEMA,
     532        exp.DynamicProperty: exp.Properties.Location.POST_CREATE,
     533        exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA,
     534        exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA,
     535        exp.EmptyProperty: exp.Properties.Location.POST_SCHEMA,
     536        exp.EncodeProperty: exp.Properties.Location.POST_EXPRESSION,
     537        exp.EngineProperty: exp.Properties.Location.POST_SCHEMA,
     538        exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA,
     539        exp.ExternalProperty: exp.Properties.Location.POST_CREATE,
     540        exp.FallbackProperty: exp.Properties.Location.POST_NAME,
     541        exp.FileFormatProperty: exp.Properties.Location.POST_WITH,
     542        exp.FreespaceProperty: exp.Properties.Location.POST_NAME,
     543        exp.GlobalProperty: exp.Properties.Location.POST_CREATE,
     544        exp.HeapProperty: exp.Properties.Location.POST_WITH,
     545        exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA,
     546        exp.IcebergProperty: exp.Properties.Location.POST_CREATE,
     547        exp.IncludeProperty: exp.Properties.Location.POST_SCHEMA,
     548        exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA,
     549        exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME,
     550        exp.JournalProperty: exp.Properties.Location.POST_NAME,
     551        exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA,
     552        exp.LikeProperty: exp.Properties.Location.POST_SCHEMA,
     553        exp.LocationProperty: exp.Properties.Location.POST_SCHEMA,
     554        exp.LockProperty: exp.Properties.Location.POST_SCHEMA,
     555        exp.LockingProperty: exp.Properties.Location.POST_ALIAS,
     556        exp.LogProperty: exp.Properties.Location.POST_NAME,
     557        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
     558        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
     559        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
     560        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
     561        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
     562        exp.Order: exp.Properties.Location.POST_SCHEMA,
     563        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
     564        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
     565        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
     566        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
     567        exp.Property: exp.Properties.Location.POST_WITH,
     568        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
     569        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
     570        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
     571        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
     572        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
     573        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
     574        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
     575        exp.SecureProperty: exp.Properties.Location.POST_CREATE,
     576        exp.SecurityProperty: exp.Properties.Location.POST_SCHEMA,
     577        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
     578        exp.Set: exp.Properties.Location.POST_SCHEMA,
     579        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
     580        exp.SetProperty: exp.Properties.Location.POST_CREATE,
     581        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
     582        exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION,
     583        exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION,
     584        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
     585        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
     586        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
     587        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
     588        exp.StreamingTableProperty: exp.Properties.Location.POST_CREATE,
     589        exp.StrictProperty: exp.Properties.Location.POST_SCHEMA,
     590        exp.Tags: exp.Properties.Location.POST_WITH,
     591        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
     592        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
     593        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
     594        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
     595        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
     596        exp.UnloggedProperty: exp.Properties.Location.POST_CREATE,
     597        exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA,
     598        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
     599        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
     600        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
     601        exp.WithProcedureOptions: exp.Properties.Location.POST_SCHEMA,
     602        exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA,
     603        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
     604    }
     605
     606    # Keywords that can't be used as unquoted identifier names
     607    RESERVED_KEYWORDS: t.Set[str] = set()
     608
     609    # Expressions whose comments are separated from them for better formatting
     610    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     611        exp.Command,
     612        exp.Create,
     613        exp.Describe,
     614        exp.Delete,
     615        exp.Drop,
     616        exp.From,
     617        exp.Insert,
     618        exp.Join,
     619        exp.MultitableInserts,
     620        exp.Select,
     621        exp.SetOperation,
     622        exp.Update,
     623        exp.Where,
     624        exp.With,
     625    )
     626
     627    # Expressions that should not have their comments generated in maybe_comment
     628    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
     629        exp.Binary,
     630        exp.SetOperation,
     631    )
     632
     633    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
     634    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
     635        exp.Column,
     636        exp.Literal,
     637        exp.Neg,
     638        exp.Paren,
     639    )
     640
     641    PARAMETERIZABLE_TEXT_TYPES = {
     642        exp.DataType.Type.NVARCHAR,
     643        exp.DataType.Type.VARCHAR,
     644        exp.DataType.Type.CHAR,
     645        exp.DataType.Type.NCHAR,
     646    }
     647
     648    # Expressions that need to have all CTEs under them bubbled up to them
     649    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
     650
     651    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
     652
     653    __slots__ = (
     654        "pretty",
     655        "identify",
     656        "normalize",
     657        "pad",
     658        "_indent",
     659        "normalize_functions",
     660        "unsupported_level",
     661        "max_unsupported",
     662        "leading_comma",
     663        "max_text_width",
     664        "comments",
     665        "dialect",
     666        "unsupported_messages",
     667        "_escaped_quote_end",
     668        "_escaped_identifier_end",
     669        "_next_name",
     670        "_identifier_start",
     671        "_identifier_end",
     672        "_quote_json_path_key_using_brackets",
     673    )
     674
     675    def __init__(
     676        self,
     677        pretty: t.Optional[bool] = None,
     678        identify: str | bool = False,
     679        normalize: bool = False,
     680        pad: int = 2,
     681        indent: int = 2,
     682        normalize_functions: t.Optional[str | bool] = None,
     683        unsupported_level: ErrorLevel = ErrorLevel.WARN,
     684        max_unsupported: int = 3,
     685        leading_comma: bool = False,
     686        max_text_width: int = 80,
     687        comments: bool = True,
     688        dialect: DialectType = None,
     689    ):
     690        import sqlglot
     691        from sqlglot.dialects import Dialect
     692
     693        self.pretty = pretty if pretty is not None else sqlglot.pretty
     694        self.identify = identify
     695        self.normalize = normalize
     696        self.pad = pad
     697        self._indent = indent
     698        self.unsupported_level = unsupported_level
     699        self.max_unsupported = max_unsupported
     700        self.leading_comma = leading_comma
     701        self.max_text_width = max_text_width
     702        self.comments = comments
     703        self.dialect = Dialect.get_or_raise(dialect)
     704
     705        # This is both a Dialect property and a Generator argument, so we prioritize the latter
     706        self.normalize_functions = (
     707            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
     708        )
     709
     710        self.unsupported_messages: t.List[str] = []
     711        self._escaped_quote_end: str = (
     712            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
     713        )
     714        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
     715
     716        self._next_name = name_sequence("_t")
     717
     718        self._identifier_start = self.dialect.IDENTIFIER_START
     719        self._identifier_end = self.dialect.IDENTIFIER_END
     720
     721        self._quote_json_path_key_using_brackets = True
     722
     723    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
     724        """
     725        Generates the SQL string corresponding to the given syntax tree.
     726
     727        Args:
     728            expression: The syntax tree.
     729            copy: Whether to copy the expression. The generator performs mutations so
     730                it is safer to copy.
     731
     732        Returns:
     733            The SQL string corresponding to `expression`.
     734        """
     735        if copy:
     736            expression = expression.copy()
     737
     738        expression = self.preprocess(expression)
     739
     740        self.unsupported_messages = []
     741        sql = self.sql(expression).strip()
     742
     743        if self.pretty:
     744            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
     745
     746        if self.unsupported_level == ErrorLevel.IGNORE:
     747            return sql
     748
     749        if self.unsupported_level == ErrorLevel.WARN:
     750            for msg in self.unsupported_messages:
     751                logger.warning(msg)
     752        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
     753            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
     754
     755        return sql
     756
     757    def preprocess(self, expression: exp.Expression) -> exp.Expression:
     758        """Apply generic preprocessing transformations to a given expression."""
     759        expression = self._move_ctes_to_top_level(expression)
     760
     761        if self.ENSURE_BOOLS:
     762            from sqlglot.transforms import ensure_bools
     763
     764            expression = ensure_bools(expression)
     765
     766        return expression
     767
     768    def _move_ctes_to_top_level(self, expression: E) -> E:
     769        if (
     770            not expression.parent
     771            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
     772            and any(node.parent is not expression for node in expression.find_all(exp.With))
     773        ):
     774            from sqlglot.transforms import move_ctes_to_top_level
     775
     776            expression = move_ctes_to_top_level(expression)
     777        return expression
     778
     779    def unsupported(self, message: str) -> None:
     780        if self.unsupported_level == ErrorLevel.IMMEDIATE:
     781            raise UnsupportedError(message)
     782        self.unsupported_messages.append(message)
     783
     784    def sep(self, sep: str = " ") -> str:
     785        return f"{sep.strip()}\n" if self.pretty else sep
     786
     787    def seg(self, sql: str, sep: str = " ") -> str:
     788        return f"{self.sep(sep)}{sql}"
     789
     790    def pad_comment(self, comment: str) -> str:
     791        comment = " " + comment if comment[0].strip() else comment
     792        comment = comment + " " if comment[-1].strip() else comment
     793        return comment
     794
     795    def maybe_comment(
     796        self,
     797        sql: str,
     798        expression: t.Optional[exp.Expression] = None,
     799        comments: t.Optional[t.List[str]] = None,
     800        separated: bool = False,
     801    ) -> str:
     802        comments = (
     803            ((expression and expression.comments) if comments is None else comments)  # type: ignore
     804            if self.comments
     805            else None
     806        )
     807
     808        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
     809            return sql
     810
     811        comments_sql = " ".join(
     812            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
     813        )
     814
     815        if not comments_sql:
     816            return sql
     817
     818        comments_sql = self._replace_line_breaks(comments_sql)
     819
     820        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
     821            return (
     822                f"{self.sep()}{comments_sql}{sql}"
     823                if not sql or sql[0].isspace()
     824                else f"{comments_sql}{self.sep()}{sql}"
     825            )
     826
     827        return f"{sql} {comments_sql}"
     828
     829    def wrap(self, expression: exp.Expression | str) -> str:
     830        this_sql = (
     831            self.sql(expression)
     832            if isinstance(expression, exp.UNWRAPPED_QUERIES)
     833            else self.sql(expression, "this")
     834        )
     835        if not this_sql:
     836            return "()"
     837
     838        this_sql = self.indent(this_sql, level=1, pad=0)
     839        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
     840
     841    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
     842        original = self.identify
     843        self.identify = False
     844        result = func(*args, **kwargs)
     845        self.identify = original
     846        return result
     847
     848    def normalize_func(self, name: str) -> str:
     849        if self.normalize_functions == "upper" or self.normalize_functions is True:
     850            return name.upper()
     851        if self.normalize_functions == "lower":
     852            return name.lower()
     853        return name
     854
     855    def indent(
     856        self,
     857        sql: str,
     858        level: int = 0,
     859        pad: t.Optional[int] = None,
     860        skip_first: bool = False,
     861        skip_last: bool = False,
     862    ) -> str:
     863        if not self.pretty or not sql:
     864            return sql
     865
     866        pad = self.pad if pad is None else pad
     867        lines = sql.split("\n")
     868
     869        return "\n".join(
     870            (
     871                line
     872                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
     873                else f"{' ' * (level * self._indent + pad)}{line}"
     874            )
     875            for i, line in enumerate(lines)
     876        )
     877
     878    def sql(
     879        self,
     880        expression: t.Optional[str | exp.Expression],
     881        key: t.Optional[str] = None,
     882        comment: bool = True,
     883    ) -> str:
     884        if not expression:
     885            return ""
     886
     887        if isinstance(expression, str):
     888            return expression
     889
     890        if key:
     891            value = expression.args.get(key)
     892            if value:
     893                return self.sql(value)
     894            return ""
     895
     896        transform = self.TRANSFORMS.get(expression.__class__)
     897
     898        if callable(transform):
     899            sql = transform(self, expression)
     900        elif isinstance(expression, exp.Expression):
     901            exp_handler_name = f"{expression.key}_sql"
     902
     903            if hasattr(self, exp_handler_name):
     904                sql = getattr(self, exp_handler_name)(expression)
     905            elif isinstance(expression, exp.Func):
     906                sql = self.function_fallback_sql(expression)
     907            elif isinstance(expression, exp.Property):
     908                sql = self.property_sql(expression)
     909            else:
     910                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
     911        else:
     912            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
     913
     914        return self.maybe_comment(sql, expression) if self.comments and comment else sql
     915
     916    def uncache_sql(self, expression: exp.Uncache) -> str:
     917        table = self.sql(expression, "this")
     918        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
     919        return f"UNCACHE TABLE{exists_sql} {table}"
     920
     921    def cache_sql(self, expression: exp.Cache) -> str:
     922        lazy = " LAZY" if expression.args.get("lazy") else ""
     923        table = self.sql(expression, "this")
     924        options = expression.args.get("options")
     925        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
     926        sql = self.sql(expression, "expression")
     927        sql = f" AS{self.sep()}{sql}" if sql else ""
     928        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
     929        return self.prepend_ctes(expression, sql)
     930
     931    def characterset_sql(self, expression: exp.CharacterSet) -> str:
     932        if isinstance(expression.parent, exp.Cast):
     933            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
     934        default = "DEFAULT " if expression.args.get("default") else ""
     935        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
     936
     937    def column_parts(self, expression: exp.Column) -> str:
     938        return ".".join(
     939            self.sql(part)
     940            for part in (
     941                expression.args.get("catalog"),
     942                expression.args.get("db"),
     943                expression.args.get("table"),
     944                expression.args.get("this"),
     945            )
     946            if part
     947        )
     948
     949    def column_sql(self, expression: exp.Column) -> str:
     950        join_mark = " (+)" if expression.args.get("join_mark") else ""
     951
     952        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
     953            join_mark = ""
     954            self.unsupported("Outer join syntax using the (+) operator is not supported.")
     955
     956        return f"{self.column_parts(expression)}{join_mark}"
     957
     958    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
     959        this = self.sql(expression, "this")
     960        this = f" {this}" if this else ""
     961        position = self.sql(expression, "position")
     962        return f"{position}{this}"
     963
     964    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
     965        column = self.sql(expression, "this")
     966        kind = self.sql(expression, "kind")
     967        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
     968        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
     969        kind = f"{sep}{kind}" if kind else ""
     970        constraints = f" {constraints}" if constraints else ""
     971        position = self.sql(expression, "position")
     972        position = f" {position}" if position else ""
     973
     974        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
     975            kind = ""
     976
     977        return f"{exists}{column}{kind}{constraints}{position}"
     978
     979    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
     980        this = self.sql(expression, "this")
     981        kind_sql = self.sql(expression, "kind").strip()
     982        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
     983
     984    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
     985        this = self.sql(expression, "this")
     986        if expression.args.get("not_null"):
     987            persisted = " PERSISTED NOT NULL"
     988        elif expression.args.get("persisted"):
     989            persisted = " PERSISTED"
     990        else:
     991            persisted = ""
     992        return f"AS {this}{persisted}"
     993
     994    def autoincrementcolumnconstraint_sql(self, _) -> str:
     995        return self.token_sql(TokenType.AUTO_INCREMENT)
     996
     997    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     998        if isinstance(expression.this, list):
     999            this = self.wrap(self.expressions(expression, key="this", flat=True))
    1000        else:
    1001            this = self.sql(expression, "this")
    1002
    1003        return f"COMPRESS {this}"
    1004
    1005    def generatedasidentitycolumnconstraint_sql(
    1006        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1007    ) -> str:
    1008        this = ""
    1009        if expression.this is not None:
    1010            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1011            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1012
    1013        start = expression.args.get("start")
    1014        start = f"START WITH {start}" if start else ""
    1015        increment = expression.args.get("increment")
    1016        increment = f" INCREMENT BY {increment}" if increment else ""
    1017        minvalue = expression.args.get("minvalue")
    1018        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1019        maxvalue = expression.args.get("maxvalue")
    1020        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1021        cycle = expression.args.get("cycle")
    1022        cycle_sql = ""
    1023
    1024        if cycle is not None:
    1025            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1026            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1027
    1028        sequence_opts = ""
    1029        if start or increment or cycle_sql:
    1030            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1031            sequence_opts = f" ({sequence_opts.strip()})"
    1032
    1033        expr = self.sql(expression, "expression")
    1034        expr = f"({expr})" if expr else "IDENTITY"
    1035
    1036        return f"GENERATED{this} AS {expr}{sequence_opts}"
    1037
    1038    def generatedasrowcolumnconstraint_sql(
    1039        self, expression: exp.GeneratedAsRowColumnConstraint
    1040    ) -> str:
    1041        start = "START" if expression.args.get("start") else "END"
    1042        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1043        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
    1044
    1045    def periodforsystemtimeconstraint_sql(
    1046        self, expression: exp.PeriodForSystemTimeConstraint
    1047    ) -> str:
    1048        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
    1049
    1050    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
    1051        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
    1052
    1053    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
    1054        return f"AS {self.sql(expression, 'this')}"
    1055
    1056    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1057        desc = expression.args.get("desc")
    1058        if desc is not None:
    1059            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1060        return "PRIMARY KEY"
    1061
    1062    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1063        this = self.sql(expression, "this")
    1064        this = f" {this}" if this else ""
    1065        index_type = expression.args.get("index_type")
    1066        index_type = f" USING {index_type}" if index_type else ""
    1067        on_conflict = self.sql(expression, "on_conflict")
    1068        on_conflict = f" {on_conflict}" if on_conflict else ""
    1069        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1070        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}"
    1071
    1072    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
    1073        return self.sql(expression, "this")
    1074
    1075    def create_sql(self, expression: exp.Create) -> str:
    1076        kind = self.sql(expression, "kind")
    1077        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1078        properties = expression.args.get("properties")
    1079        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1080
    1081        this = self.createable_sql(expression, properties_locs)
    1082
    1083        properties_sql = ""
    1084        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1085            exp.Properties.Location.POST_WITH
    1086        ):
    1087            properties_sql = self.sql(
    1088                exp.Properties(
    1089                    expressions=[
    1090                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1091                        *properties_locs[exp.Properties.Location.POST_WITH],
    1092                    ]
    1093                )
    1094            )
    1095
    1096            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1097                properties_sql = self.sep() + properties_sql
    1098            elif not self.pretty:
    1099                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1100                properties_sql = f" {properties_sql}"
    1101
    1102        begin = " BEGIN" if expression.args.get("begin") else ""
    1103        end = " END" if expression.args.get("end") else ""
    1104
    1105        expression_sql = self.sql(expression, "expression")
    1106        if expression_sql:
    1107            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1108
    1109            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1110                postalias_props_sql = ""
    1111                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1112                    postalias_props_sql = self.properties(
    1113                        exp.Properties(
    1114                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1115                        ),
    1116                        wrapped=False,
    1117                    )
    1118                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1119                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1120
    1121        postindex_props_sql = ""
    1122        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1123            postindex_props_sql = self.properties(
    1124                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1125                wrapped=False,
    1126                prefix=" ",
    1127            )
    1128
    1129        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1130        indexes = f" {indexes}" if indexes else ""
    1131        index_sql = indexes + postindex_props_sql
    1132
    1133        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1134        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1135        unique = " UNIQUE" if expression.args.get("unique") else ""
    1136
    1137        clustered = expression.args.get("clustered")
    1138        if clustered is None:
    1139            clustered_sql = ""
    1140        elif clustered:
    1141            clustered_sql = " CLUSTERED COLUMNSTORE"
    1142        else:
    1143            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1144
    1145        postcreate_props_sql = ""
    1146        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1147            postcreate_props_sql = self.properties(
    1148                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1149                sep=" ",
    1150                prefix=" ",
    1151                wrapped=False,
    1152            )
    1153
    1154        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1155
    1156        postexpression_props_sql = ""
    1157        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1158            postexpression_props_sql = self.properties(
    1159                exp.Properties(
    1160                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1161                ),
    1162                sep=" ",
    1163                prefix=" ",
    1164                wrapped=False,
    1165            )
    1166
    1167        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1168        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1169        no_schema_binding = (
    1170            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1171        )
    1172
    1173        clone = self.sql(expression, "clone")
    1174        clone = f" {clone}" if clone else ""
    1175
    1176        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1177            properties_expression = f"{expression_sql}{properties_sql}"
    1178        else:
    1179            properties_expression = f"{properties_sql}{expression_sql}"
    1180
    1181        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1182        return self.prepend_ctes(expression, expression_sql)
    1183
    1184    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1185        start = self.sql(expression, "start")
    1186        start = f"START WITH {start}" if start else ""
    1187        increment = self.sql(expression, "increment")
    1188        increment = f" INCREMENT BY {increment}" if increment else ""
    1189        minvalue = self.sql(expression, "minvalue")
    1190        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1191        maxvalue = self.sql(expression, "maxvalue")
    1192        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1193        owned = self.sql(expression, "owned")
    1194        owned = f" OWNED BY {owned}" if owned else ""
    1195
    1196        cache = expression.args.get("cache")
    1197        if cache is None:
    1198            cache_str = ""
    1199        elif cache is True:
    1200            cache_str = " CACHE"
    1201        else:
    1202            cache_str = f" CACHE {cache}"
    1203
    1204        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1205        options = f" {options}" if options else ""
    1206
    1207        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
    1208
    1209    def clone_sql(self, expression: exp.Clone) -> str:
    1210        this = self.sql(expression, "this")
    1211        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1212        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1213        return f"{shallow}{keyword} {this}"
    1214
    1215    def describe_sql(self, expression: exp.Describe) -> str:
    1216        style = expression.args.get("style")
    1217        style = f" {style}" if style else ""
    1218        partition = self.sql(expression, "partition")
    1219        partition = f" {partition}" if partition else ""
    1220        format = self.sql(expression, "format")
    1221        format = f" {format}" if format else ""
    1222
    1223        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"
    1224
    1225    def heredoc_sql(self, expression: exp.Heredoc) -> str:
    1226        tag = self.sql(expression, "tag")
    1227        return f"${tag}${self.sql(expression, 'this')}${tag}$"
    1228
    1229    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1230        with_ = self.sql(expression, "with")
    1231        if with_:
    1232            sql = f"{with_}{self.sep()}{sql}"
    1233        return sql
    1234
    1235    def with_sql(self, expression: exp.With) -> str:
    1236        sql = self.expressions(expression, flat=True)
    1237        recursive = (
    1238            "RECURSIVE "
    1239            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1240            else ""
    1241        )
    1242
    1243        return f"WITH {recursive}{sql}"
    1244
    1245    def cte_sql(self, expression: exp.CTE) -> str:
    1246        alias = expression.args.get("alias")
    1247        if alias:
    1248            alias.add_comments(expression.pop_comments())
    1249
    1250        alias_sql = self.sql(expression, "alias")
    1251
    1252        materialized = expression.args.get("materialized")
    1253        if materialized is False:
    1254            materialized = "NOT MATERIALIZED "
    1255        elif materialized:
    1256            materialized = "MATERIALIZED "
    1257
    1258        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"
    1259
    1260    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1261        alias = self.sql(expression, "this")
    1262        columns = self.expressions(expression, key="columns", flat=True)
    1263        columns = f"({columns})" if columns else ""
    1264
    1265        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1266            columns = ""
    1267            self.unsupported("Named columns are not supported in table alias.")
    1268
    1269        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1270            alias = self._next_name()
    1271
    1272        return f"{alias}{columns}"
    1273
    1274    def bitstring_sql(self, expression: exp.BitString) -> str:
    1275        this = self.sql(expression, "this")
    1276        if self.dialect.BIT_START:
    1277            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1278        return f"{int(this, 2)}"
    1279
    1280    def hexstring_sql(self, expression: exp.HexString) -> str:
    1281        this = self.sql(expression, "this")
    1282        if self.dialect.HEX_START:
    1283            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1284        return f"{int(this, 16)}"
    1285
    1286    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1287        this = self.sql(expression, "this")
    1288        if self.dialect.BYTE_START:
    1289            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1290        return this
    1291
    1292    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1293        this = self.sql(expression, "this")
    1294        escape = expression.args.get("escape")
    1295
    1296        if self.dialect.UNICODE_START:
    1297            escape_substitute = r"\\\1"
    1298            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1299        else:
    1300            escape_substitute = r"\\u\1"
    1301            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1302
    1303        if escape:
    1304            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1305            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1306        else:
    1307            escape_pattern = ESCAPED_UNICODE_RE
    1308            escape_sql = ""
    1309
    1310        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1311            this = escape_pattern.sub(escape_substitute, this)
    1312
    1313        return f"{left_quote}{this}{right_quote}{escape_sql}"
    1314
    1315    def rawstring_sql(self, expression: exp.RawString) -> str:
    1316        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
    1317        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
    1318
    1319    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1320        this = self.sql(expression, "this")
    1321        specifier = self.sql(expression, "expression")
    1322        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1323        return f"{this}{specifier}"
    1324
    1325    def datatype_sql(self, expression: exp.DataType) -> str:
    1326        nested = ""
    1327        values = ""
    1328        interior = self.expressions(expression, flat=True)
    1329
    1330        type_value = expression.this
    1331        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1332            type_sql = self.sql(expression, "kind")
    1333        else:
    1334            type_sql = (
    1335                self.TYPE_MAPPING.get(type_value, type_value.value)
    1336                if isinstance(type_value, exp.DataType.Type)
    1337                else type_value
    1338            )
    1339
    1340        if interior:
    1341            if expression.args.get("nested"):
    1342                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1343                if expression.args.get("values") is not None:
    1344                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1345                    values = self.expressions(expression, key="values", flat=True)
    1346                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1347            elif type_value == exp.DataType.Type.INTERVAL:
    1348                nested = f" {interior}"
    1349            else:
    1350                nested = f"({interior})"
    1351
    1352        type_sql = f"{type_sql}{nested}{values}"
    1353        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1354            exp.DataType.Type.TIMETZ,
    1355            exp.DataType.Type.TIMESTAMPTZ,
    1356        ):
    1357            type_sql = f"{type_sql} WITH TIME ZONE"
    1358
    1359        return type_sql
    1360
    1361    def directory_sql(self, expression: exp.Directory) -> str:
    1362        local = "LOCAL " if expression.args.get("local") else ""
    1363        row_format = self.sql(expression, "row_format")
    1364        row_format = f" {row_format}" if row_format else ""
    1365        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
    1366
    1367    def delete_sql(self, expression: exp.Delete) -> str:
    1368        this = self.sql(expression, "this")
    1369        this = f" FROM {this}" if this else ""
    1370        using = self.sql(expression, "using")
    1371        using = f" USING {using}" if using else ""
    1372        cluster = self.sql(expression, "cluster")
    1373        cluster = f" {cluster}" if cluster else ""
    1374        where = self.sql(expression, "where")
    1375        returning = self.sql(expression, "returning")
    1376        limit = self.sql(expression, "limit")
    1377        tables = self.expressions(expression, key="tables")
    1378        tables = f" {tables}" if tables else ""
    1379        if self.RETURNING_END:
    1380            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1381        else:
    1382            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1383        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
    1384
    1385    def drop_sql(self, expression: exp.Drop) -> str:
    1386        this = self.sql(expression, "this")
    1387        expressions = self.expressions(expression, flat=True)
    1388        expressions = f" ({expressions})" if expressions else ""
    1389        kind = expression.args["kind"]
    1390        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1391        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1392        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1393        on_cluster = self.sql(expression, "cluster")
    1394        on_cluster = f" {on_cluster}" if on_cluster else ""
    1395        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1396        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1397        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1398        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1399        purge = " PURGE" if expression.args.get("purge") else ""
    1400        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
    1401
    1402    def set_operation(self, expression: exp.SetOperation) -> str:
    1403        op_type = type(expression)
    1404        op_name = op_type.key.upper()
    1405
    1406        distinct = expression.args.get("distinct")
    1407        if (
    1408            distinct is False
    1409            and op_type in (exp.Except, exp.Intersect)
    1410            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1411        ):
    1412            self.unsupported(f"{op_name} ALL is not supported")
    1413
    1414        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1415
    1416        if distinct is None:
    1417            distinct = default_distinct
    1418            if distinct is None:
    1419                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1420
    1421        if distinct is default_distinct:
    1422            kind = ""
    1423        else:
    1424            kind = " DISTINCT" if distinct else " ALL"
    1425
    1426        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1427        return f"{op_name}{kind}{by_name}"
    1428
    1429    def set_operations(self, expression: exp.SetOperation) -> str:
    1430        if not self.SET_OP_MODIFIERS:
    1431            limit = expression.args.get("limit")
    1432            order = expression.args.get("order")
    1433
    1434            if limit or order:
    1435                select = self._move_ctes_to_top_level(
    1436                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1437                )
    1438
    1439                if limit:
    1440                    select = select.limit(limit.pop(), copy=False)
    1441                if order:
    1442                    select = select.order_by(order.pop(), copy=False)
    1443                return self.sql(select)
    1444
    1445        sqls: t.List[str] = []
    1446        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1447
    1448        while stack:
    1449            node = stack.pop()
    1450
    1451            if isinstance(node, exp.SetOperation):
    1452                stack.append(node.expression)
    1453                stack.append(
    1454                    self.maybe_comment(
    1455                        self.set_operation(node), comments=node.comments, separated=True
    1456                    )
    1457                )
    1458                stack.append(node.this)
    1459            else:
    1460                sqls.append(self.sql(node))
    1461
    1462        this = self.sep().join(sqls)
    1463        this = self.query_modifiers(expression, this)
    1464        return self.prepend_ctes(expression, this)
    1465
    1466    def fetch_sql(self, expression: exp.Fetch) -> str:
    1467        direction = expression.args.get("direction")
    1468        direction = f" {direction}" if direction else ""
    1469        count = self.sql(expression, "count")
    1470        count = f" {count}" if count else ""
    1471        if expression.args.get("percent"):
    1472            count = f"{count} PERCENT"
    1473        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1474        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
    1475
    1476    def filter_sql(self, expression: exp.Filter) -> str:
    1477        if self.AGGREGATE_FILTER_SUPPORTED:
    1478            this = self.sql(expression, "this")
    1479            where = self.sql(expression, "expression").strip()
    1480            return f"{this} FILTER({where})"
    1481
    1482        agg = expression.this
    1483        agg_arg = agg.this
    1484        cond = expression.expression.this
    1485        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1486        return self.sql(agg)
    1487
    1488    def hint_sql(self, expression: exp.Hint) -> str:
    1489        if not self.QUERY_HINTS:
    1490            self.unsupported("Hints are not supported")
    1491            return ""
    1492
    1493        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
    1494
    1495    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1496        using = self.sql(expression, "using")
    1497        using = f" USING {using}" if using else ""
    1498        columns = self.expressions(expression, key="columns", flat=True)
    1499        columns = f"({columns})" if columns else ""
    1500        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1501        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1502        where = self.sql(expression, "where")
    1503        include = self.expressions(expression, key="include", flat=True)
    1504        if include:
    1505            include = f" INCLUDE ({include})"
    1506        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1507        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1508        tablespace = self.sql(expression, "tablespace")
    1509        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1510        on = self.sql(expression, "on")
    1511        on = f" ON {on}" if on else ""
    1512
    1513        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
    1514
    1515    def index_sql(self, expression: exp.Index) -> str:
    1516        unique = "UNIQUE " if expression.args.get("unique") else ""
    1517        primary = "PRIMARY " if expression.args.get("primary") else ""
    1518        amp = "AMP " if expression.args.get("amp") else ""
    1519        name = self.sql(expression, "this")
    1520        name = f"{name} " if name else ""
    1521        table = self.sql(expression, "table")
    1522        table = f"{self.INDEX_ON} {table}" if table else ""
    1523
    1524        index = "INDEX " if not table else ""
    1525
    1526        params = self.sql(expression, "params")
    1527        return f"{unique}{primary}{amp}{index}{name}{table}{params}"
    1528
    1529    def identifier_sql(self, expression: exp.Identifier) -> str:
    1530        text = expression.name
    1531        lower = text.lower()
    1532        text = lower if self.normalize and not expression.quoted else text
    1533        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1534        if (
    1535            expression.quoted
    1536            or self.dialect.can_identify(text, self.identify)
    1537            or lower in self.RESERVED_KEYWORDS
    1538            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1539        ):
    1540            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1541        return text
    1542
    1543    def hex_sql(self, expression: exp.Hex) -> str:
    1544        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1545        if self.dialect.HEX_LOWERCASE:
    1546            text = self.func("LOWER", text)
    1547
    1548        return text
    1549
    1550    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1551        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1552        if not self.dialect.HEX_LOWERCASE:
    1553            text = self.func("LOWER", text)
    1554        return text
    1555
    1556    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1557        input_format = self.sql(expression, "input_format")
    1558        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1559        output_format = self.sql(expression, "output_format")
    1560        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1561        return self.sep().join((input_format, output_format))
    1562
    1563    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
    1564        string = self.sql(exp.Literal.string(expression.name))
    1565        return f"{prefix}{string}"
    1566
    1567    def partition_sql(self, expression: exp.Partition) -> str:
    1568        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
    1569        return f"{partition_keyword}({self.expressions(expression, flat=True)})"
    1570
    1571    def properties_sql(self, expression: exp.Properties) -> str:
    1572        root_properties = []
    1573        with_properties = []
    1574
    1575        for p in expression.expressions:
    1576            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1577            if p_loc == exp.Properties.Location.POST_WITH:
    1578                with_properties.append(p)
    1579            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1580                root_properties.append(p)
    1581
    1582        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1583        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1584
    1585        if root_props and with_props and not self.pretty:
    1586            with_props = " " + with_props
    1587
    1588        return root_props + with_props
    1589
    1590    def root_properties(self, properties: exp.Properties) -> str:
    1591        if properties.expressions:
    1592            return self.expressions(properties, indent=False, sep=" ")
    1593        return ""
    1594
    1595    def properties(
    1596        self,
    1597        properties: exp.Properties,
    1598        prefix: str = "",
    1599        sep: str = ", ",
    1600        suffix: str = "",
    1601        wrapped: bool = True,
    1602    ) -> str:
    1603        if properties.expressions:
    1604            expressions = self.expressions(properties, sep=sep, indent=False)
    1605            if expressions:
    1606                expressions = self.wrap(expressions) if wrapped else expressions
    1607                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1608        return ""
    1609
    1610    def with_properties(self, properties: exp.Properties) -> str:
    1611        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))
    1612
    1613    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1614        properties_locs = defaultdict(list)
    1615        for p in properties.expressions:
    1616            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1617            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1618                properties_locs[p_loc].append(p)
    1619            else:
    1620                self.unsupported(f"Unsupported property {p.key}")
    1621
    1622        return properties_locs
    1623
    1624    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1625        if isinstance(expression.this, exp.Dot):
    1626            return self.sql(expression, "this")
    1627        return f"'{expression.name}'" if string_key else expression.name
    1628
    1629    def property_sql(self, expression: exp.Property) -> str:
    1630        property_cls = expression.__class__
    1631        if property_cls == exp.Property:
    1632            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1633
    1634        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1635        if not property_name:
    1636            self.unsupported(f"Unsupported property {expression.key}")
    1637
    1638        return f"{property_name}={self.sql(expression, 'this')}"
    1639
    1640    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1641        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1642            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1643            options = f" {options}" if options else ""
    1644
    1645            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1646            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1647                like = f"({like})"
    1648
    1649            return like
    1650
    1651        if expression.expressions:
    1652            self.unsupported("Transpilation of LIKE property options is unsupported")
    1653
    1654        select = exp.select("*").from_(expression.this).limit(0)
    1655        return f"AS {self.sql(select)}"
    1656
    1657    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1658        no = "NO " if expression.args.get("no") else ""
    1659        protection = " PROTECTION" if expression.args.get("protection") else ""
    1660        return f"{no}FALLBACK{protection}"
    1661
    1662    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1663        no = "NO " if expression.args.get("no") else ""
    1664        local = expression.args.get("local")
    1665        local = f"{local} " if local else ""
    1666        dual = "DUAL " if expression.args.get("dual") else ""
    1667        before = "BEFORE " if expression.args.get("before") else ""
    1668        after = "AFTER " if expression.args.get("after") else ""
    1669        return f"{no}{local}{dual}{before}{after}JOURNAL"
    1670
    1671    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1672        freespace = self.sql(expression, "this")
    1673        percent = " PERCENT" if expression.args.get("percent") else ""
    1674        return f"FREESPACE={freespace}{percent}"
    1675
    1676    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1677        if expression.args.get("default"):
    1678            property = "DEFAULT"
    1679        elif expression.args.get("on"):
    1680            property = "ON"
    1681        else:
    1682            property = "OFF"
    1683        return f"CHECKSUM={property}"
    1684
    1685    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1686        if expression.args.get("no"):
    1687            return "NO MERGEBLOCKRATIO"
    1688        if expression.args.get("default"):
    1689            return "DEFAULT MERGEBLOCKRATIO"
    1690
    1691        percent = " PERCENT" if expression.args.get("percent") else ""
    1692        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
    1693
    1694    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1695        default = expression.args.get("default")
    1696        minimum = expression.args.get("minimum")
    1697        maximum = expression.args.get("maximum")
    1698        if default or minimum or maximum:
    1699            if default:
    1700                prop = "DEFAULT"
    1701            elif minimum:
    1702                prop = "MINIMUM"
    1703            else:
    1704                prop = "MAXIMUM"
    1705            return f"{prop} DATABLOCKSIZE"
    1706        units = expression.args.get("units")
    1707        units = f" {units}" if units else ""
    1708        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
    1709
    1710    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1711        autotemp = expression.args.get("autotemp")
    1712        always = expression.args.get("always")
    1713        default = expression.args.get("default")
    1714        manual = expression.args.get("manual")
    1715        never = expression.args.get("never")
    1716
    1717        if autotemp is not None:
    1718            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1719        elif always:
    1720            prop = "ALWAYS"
    1721        elif default:
    1722            prop = "DEFAULT"
    1723        elif manual:
    1724            prop = "MANUAL"
    1725        elif never:
    1726            prop = "NEVER"
    1727        return f"BLOCKCOMPRESSION={prop}"
    1728
    1729    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1730        no = expression.args.get("no")
    1731        no = " NO" if no else ""
    1732        concurrent = expression.args.get("concurrent")
    1733        concurrent = " CONCURRENT" if concurrent else ""
    1734        target = self.sql(expression, "target")
    1735        target = f" {target}" if target else ""
    1736        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
    1737
    1738    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1739        if isinstance(expression.this, list):
    1740            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1741        if expression.this:
    1742            modulus = self.sql(expression, "this")
    1743            remainder = self.sql(expression, "expression")
    1744            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1745
    1746        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1747        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1748        return f"FROM ({from_expressions}) TO ({to_expressions})"
    1749
    1750    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1751        this = self.sql(expression, "this")
    1752
    1753        for_values_or_default = expression.expression
    1754        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1755            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1756        else:
    1757            for_values_or_default = " DEFAULT"
    1758
    1759        return f"PARTITION OF {this}{for_values_or_default}"
    1760
    1761    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1762        kind = expression.args.get("kind")
    1763        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1764        for_or_in = expression.args.get("for_or_in")
    1765        for_or_in = f" {for_or_in}" if for_or_in else ""
    1766        lock_type = expression.args.get("lock_type")
    1767        override = " OVERRIDE" if expression.args.get("override") else ""
    1768        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
    1769
    1770    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1771        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1772        statistics = expression.args.get("statistics")
    1773        statistics_sql = ""
    1774        if statistics is not None:
    1775            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1776        return f"{data_sql}{statistics_sql}"
    1777
    1778    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1779        this = self.sql(expression, "this")
    1780        this = f"HISTORY_TABLE={this}" if this else ""
    1781        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1782        data_consistency = (
    1783            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1784        )
    1785        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1786        retention_period = (
    1787            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1788        )
    1789
    1790        if this:
    1791            on_sql = self.func("ON", this, data_consistency, retention_period)
    1792        else:
    1793            on_sql = "ON" if expression.args.get("on") else "OFF"
    1794
    1795        sql = f"SYSTEM_VERSIONING={on_sql}"
    1796
    1797        return f"WITH({sql})" if expression.args.get("with") else sql
    1798
    1799    def insert_sql(self, expression: exp.Insert) -> str:
    1800        hint = self.sql(expression, "hint")
    1801        overwrite = expression.args.get("overwrite")
    1802
    1803        if isinstance(expression.this, exp.Directory):
    1804            this = " OVERWRITE" if overwrite else " INTO"
    1805        else:
    1806            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1807
    1808        stored = self.sql(expression, "stored")
    1809        stored = f" {stored}" if stored else ""
    1810        alternative = expression.args.get("alternative")
    1811        alternative = f" OR {alternative}" if alternative else ""
    1812        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1813        is_function = expression.args.get("is_function")
    1814        if is_function:
    1815            this = f"{this} FUNCTION"
    1816        this = f"{this} {self.sql(expression, 'this')}"
    1817
    1818        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1819        where = self.sql(expression, "where")
    1820        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1821        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1822        on_conflict = self.sql(expression, "conflict")
    1823        on_conflict = f" {on_conflict}" if on_conflict else ""
    1824        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1825        returning = self.sql(expression, "returning")
    1826
    1827        if self.RETURNING_END:
    1828            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1829        else:
    1830            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1831
    1832        partition_by = self.sql(expression, "partition")
    1833        partition_by = f" {partition_by}" if partition_by else ""
    1834        settings = self.sql(expression, "settings")
    1835        settings = f" {settings}" if settings else ""
    1836
    1837        source = self.sql(expression, "source")
    1838        source = f"TABLE {source}" if source else ""
    1839
    1840        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1841        return self.prepend_ctes(expression, sql)
    1842
    1843    def introducer_sql(self, expression: exp.Introducer) -> str:
    1844        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    1845
    1846    def kill_sql(self, expression: exp.Kill) -> str:
    1847        kind = self.sql(expression, "kind")
    1848        kind = f" {kind}" if kind else ""
    1849        this = self.sql(expression, "this")
    1850        this = f" {this}" if this else ""
    1851        return f"KILL{kind}{this}"
    1852
    1853    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1854        return expression.name
    1855
    1856    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1857        return expression.name
    1858
    1859    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1860        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1861
    1862        constraint = self.sql(expression, "constraint")
    1863        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1864
    1865        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1866        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1867        action = self.sql(expression, "action")
    1868
    1869        expressions = self.expressions(expression, flat=True)
    1870        if expressions:
    1871            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1872            expressions = f" {set_keyword}{expressions}"
    1873
    1874        where = self.sql(expression, "where")
    1875        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"
    1876
    1877    def returning_sql(self, expression: exp.Returning) -> str:
    1878        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
    1879
    1880    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1881        fields = self.sql(expression, "fields")
    1882        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1883        escaped = self.sql(expression, "escaped")
    1884        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1885        items = self.sql(expression, "collection_items")
    1886        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1887        keys = self.sql(expression, "map_keys")
    1888        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1889        lines = self.sql(expression, "lines")
    1890        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1891        null = self.sql(expression, "null")
    1892        null = f" NULL DEFINED AS {null}" if null else ""
    1893        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
    1894
    1895    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
    1896        return f"WITH ({self.expressions(expression, flat=True)})"
    1897
    1898    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1899        this = f"{self.sql(expression, 'this')} INDEX"
    1900        target = self.sql(expression, "target")
    1901        target = f" FOR {target}" if target else ""
    1902        return f"{this}{target} ({self.expressions(expression, flat=True)})"
    1903
    1904    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1905        this = self.sql(expression, "this")
    1906        kind = self.sql(expression, "kind")
    1907        expr = self.sql(expression, "expression")
    1908        return f"{this} ({kind} => {expr})"
    1909
    1910    def table_parts(self, expression: exp.Table) -> str:
    1911        return ".".join(
    1912            self.sql(part)
    1913            for part in (
    1914                expression.args.get("catalog"),
    1915                expression.args.get("db"),
    1916                expression.args.get("this"),
    1917            )
    1918            if part is not None
    1919        )
    1920
    1921    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1922        table = self.table_parts(expression)
    1923        only = "ONLY " if expression.args.get("only") else ""
    1924        partition = self.sql(expression, "partition")
    1925        partition = f" {partition}" if partition else ""
    1926        version = self.sql(expression, "version")
    1927        version = f" {version}" if version else ""
    1928        alias = self.sql(expression, "alias")
    1929        alias = f"{sep}{alias}" if alias else ""
    1930
    1931        sample = self.sql(expression, "sample")
    1932        if self.dialect.ALIAS_POST_TABLESAMPLE:
    1933            sample_pre_alias = sample
    1934            sample_post_alias = ""
    1935        else:
    1936            sample_pre_alias = ""
    1937            sample_post_alias = sample
    1938
    1939        hints = self.expressions(expression, key="hints", sep=" ")
    1940        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1941        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    1942        joins = self.indent(
    1943            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1944        )
    1945        laterals = self.expressions(expression, key="laterals", sep="")
    1946
    1947        file_format = self.sql(expression, "format")
    1948        if file_format:
    1949            pattern = self.sql(expression, "pattern")
    1950            pattern = f", PATTERN => {pattern}" if pattern else ""
    1951            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1952
    1953        ordinality = expression.args.get("ordinality") or ""
    1954        if ordinality:
    1955            ordinality = f" WITH ORDINALITY{alias}"
    1956            alias = ""
    1957
    1958        when = self.sql(expression, "when")
    1959        if when:
    1960            table = f"{table} {when}"
    1961
    1962        changes = self.sql(expression, "changes")
    1963        changes = f" {changes}" if changes else ""
    1964
    1965        rows_from = self.expressions(expression, key="rows_from")
    1966        if rows_from:
    1967            table = f"ROWS FROM {self.wrap(rows_from)}"
    1968
    1969        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"
    1970
    1971    def tablesample_sql(
    1972        self,
    1973        expression: exp.TableSample,
    1974        tablesample_keyword: t.Optional[str] = None,
    1975    ) -> str:
    1976        method = self.sql(expression, "method")
    1977        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1978        numerator = self.sql(expression, "bucket_numerator")
    1979        denominator = self.sql(expression, "bucket_denominator")
    1980        field = self.sql(expression, "bucket_field")
    1981        field = f" ON {field}" if field else ""
    1982        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1983        seed = self.sql(expression, "seed")
    1984        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1985
    1986        size = self.sql(expression, "size")
    1987        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1988            size = f"{size} ROWS"
    1989
    1990        percent = self.sql(expression, "percent")
    1991        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1992            percent = f"{percent} PERCENT"
    1993
    1994        expr = f"{bucket}{percent}{size}"
    1995        if self.TABLESAMPLE_REQUIRES_PARENS:
    1996            expr = f"({expr})"
    1997
    1998        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"
    1999
    2000    def pivot_sql(self, expression: exp.Pivot) -> str:
    2001        expressions = self.expressions(expression, flat=True)
    2002        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2003
    2004        if expression.this:
    2005            this = self.sql(expression, "this")
    2006            if not expressions:
    2007                return f"UNPIVOT {this}"
    2008
    2009            on = f"{self.seg('ON')} {expressions}"
    2010            into = self.sql(expression, "into")
    2011            into = f"{self.seg('INTO')} {into}" if into else ""
    2012            using = self.expressions(expression, key="using", flat=True)
    2013            using = f"{self.seg('USING')} {using}" if using else ""
    2014            group = self.sql(expression, "group")
    2015            return f"{direction} {this}{on}{into}{using}{group}"
    2016
    2017        alias = self.sql(expression, "alias")
    2018        alias = f" AS {alias}" if alias else ""
    2019
    2020        field = self.sql(expression, "field")
    2021
    2022        include_nulls = expression.args.get("include_nulls")
    2023        if include_nulls is not None:
    2024            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2025        else:
    2026            nulls = ""
    2027
    2028        default_on_null = self.sql(expression, "default_on_null")
    2029        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2030        return f"{self.seg(direction)}{nulls}({expressions} FOR {field}{default_on_null}){alias}"
    2031
    2032    def version_sql(self, expression: exp.Version) -> str:
    2033        this = f"FOR {expression.name}"
    2034        kind = expression.text("kind")
    2035        expr = self.sql(expression, "expression")
    2036        return f"{this} {kind} {expr}"
    2037
    2038    def tuple_sql(self, expression: exp.Tuple) -> str:
    2039        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"
    2040
    2041    def update_sql(self, expression: exp.Update) -> str:
    2042        this = self.sql(expression, "this")
    2043        set_sql = self.expressions(expression, flat=True)
    2044        from_sql = self.sql(expression, "from")
    2045        where_sql = self.sql(expression, "where")
    2046        returning = self.sql(expression, "returning")
    2047        order = self.sql(expression, "order")
    2048        limit = self.sql(expression, "limit")
    2049        if self.RETURNING_END:
    2050            expression_sql = f"{from_sql}{where_sql}{returning}"
    2051        else:
    2052            expression_sql = f"{returning}{from_sql}{where_sql}"
    2053        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2054        return self.prepend_ctes(expression, sql)
    2055
    2056    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2057        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2058
    2059        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2060        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2061            args = self.expressions(expression)
    2062            alias = self.sql(expression, "alias")
    2063            values = f"VALUES{self.seg('')}{args}"
    2064            values = (
    2065                f"({values})"
    2066                if self.WRAP_DERIVED_VALUES
    2067                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2068                else values
    2069            )
    2070            return f"{values} AS {alias}" if alias else values
    2071
    2072        # Converts `VALUES...` expression into a series of select unions.
    2073        alias_node = expression.args.get("alias")
    2074        column_names = alias_node and alias_node.columns
    2075
    2076        selects: t.List[exp.Query] = []
    2077
    2078        for i, tup in enumerate(expression.expressions):
    2079            row = tup.expressions
    2080
    2081            if i == 0 and column_names:
    2082                row = [
    2083                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2084                ]
    2085
    2086            selects.append(exp.Select(expressions=row))
    2087
    2088        if self.pretty:
    2089            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2090            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2091            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2092            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2093            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2094
    2095        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2096        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2097        return f"({unions}){alias}"
    2098
    2099    def var_sql(self, expression: exp.Var) -> str:
    2100        return self.sql(expression, "this")
    2101
    2102    @unsupported_args("expressions")
    2103    def into_sql(self, expression: exp.Into) -> str:
    2104        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2105        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2106        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
    2107
    2108    def from_sql(self, expression: exp.From) -> str:
    2109        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
    2110
    2111    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
    2112        grouping_sets = self.expressions(expression, indent=False)
    2113        return f"GROUPING SETS {self.wrap(grouping_sets)}"
    2114
    2115    def rollup_sql(self, expression: exp.Rollup) -> str:
    2116        expressions = self.expressions(expression, indent=False)
    2117        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
    2118
    2119    def cube_sql(self, expression: exp.Cube) -> str:
    2120        expressions = self.expressions(expression, indent=False)
    2121        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
    2122
    2123    def group_sql(self, expression: exp.Group) -> str:
    2124        group_by_all = expression.args.get("all")
    2125        if group_by_all is True:
    2126            modifier = " ALL"
    2127        elif group_by_all is False:
    2128            modifier = " DISTINCT"
    2129        else:
    2130            modifier = ""
    2131
    2132        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2133
    2134        grouping_sets = self.expressions(expression, key="grouping_sets")
    2135        cube = self.expressions(expression, key="cube")
    2136        rollup = self.expressions(expression, key="rollup")
    2137
    2138        groupings = csv(
    2139            self.seg(grouping_sets) if grouping_sets else "",
    2140            self.seg(cube) if cube else "",
    2141            self.seg(rollup) if rollup else "",
    2142            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2143            sep=self.GROUPINGS_SEP,
    2144        )
    2145
    2146        if (
    2147            expression.expressions
    2148            and groupings
    2149            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2150        ):
    2151            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2152
    2153        return f"{group_by}{groupings}"
    2154
    2155    def having_sql(self, expression: exp.Having) -> str:
    2156        this = self.indent(self.sql(expression, "this"))
    2157        return f"{self.seg('HAVING')}{self.sep()}{this}"
    2158
    2159    def connect_sql(self, expression: exp.Connect) -> str:
    2160        start = self.sql(expression, "start")
    2161        start = self.seg(f"START WITH {start}") if start else ""
    2162        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2163        connect = self.sql(expression, "connect")
    2164        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2165        return start + connect
    2166
    2167    def prior_sql(self, expression: exp.Prior) -> str:
    2168        return f"PRIOR {self.sql(expression, 'this')}"
    2169
    2170    def join_sql(self, expression: exp.Join) -> str:
    2171        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2172            side = None
    2173        else:
    2174            side = expression.side
    2175
    2176        op_sql = " ".join(
    2177            op
    2178            for op in (
    2179                expression.method,
    2180                "GLOBAL" if expression.args.get("global") else None,
    2181                side,
    2182                expression.kind,
    2183                expression.hint if self.JOIN_HINTS else None,
    2184            )
    2185            if op
    2186        )
    2187        match_cond = self.sql(expression, "match_condition")
    2188        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2189        on_sql = self.sql(expression, "on")
    2190        using = expression.args.get("using")
    2191
    2192        if not on_sql and using:
    2193            on_sql = csv(*(self.sql(column) for column in using))
    2194
    2195        this = expression.this
    2196        this_sql = self.sql(this)
    2197
    2198        exprs = self.expressions(expression)
    2199        if exprs:
    2200            this_sql = f"{this_sql},{self.seg(exprs)}"
    2201
    2202        if on_sql:
    2203            on_sql = self.indent(on_sql, skip_first=True)
    2204            space = self.seg(" " * self.pad) if self.pretty else " "
    2205            if using:
    2206                on_sql = f"{space}USING ({on_sql})"
    2207            else:
    2208                on_sql = f"{space}ON {on_sql}"
    2209        elif not op_sql:
    2210            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2211                return f" {this_sql}"
    2212
    2213            return f", {this_sql}"
    2214
    2215        if op_sql != "STRAIGHT_JOIN":
    2216            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2217
    2218        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
    2219
    2220    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
    2221        args = self.expressions(expression, flat=True)
    2222        args = f"({args})" if len(args.split(",")) > 1 else args
    2223        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
    2224
    2225    def lateral_op(self, expression: exp.Lateral) -> str:
    2226        cross_apply = expression.args.get("cross_apply")
    2227
    2228        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2229        if cross_apply is True:
    2230            op = "INNER JOIN "
    2231        elif cross_apply is False:
    2232            op = "LEFT JOIN "
    2233        else:
    2234            op = ""
    2235
    2236        return f"{op}LATERAL"
    2237
    2238    def lateral_sql(self, expression: exp.Lateral) -> str:
    2239        this = self.sql(expression, "this")
    2240
    2241        if expression.args.get("view"):
    2242            alias = expression.args["alias"]
    2243            columns = self.expressions(alias, key="columns", flat=True)
    2244            table = f" {alias.name}" if alias.name else ""
    2245            columns = f" AS {columns}" if columns else ""
    2246            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2247            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2248
    2249        alias = self.sql(expression, "alias")
    2250        alias = f" AS {alias}" if alias else ""
    2251        return f"{self.lateral_op(expression)} {this}{alias}"
    2252
    2253    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2254        this = self.sql(expression, "this")
    2255
    2256        args = [
    2257            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2258            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2259            if e
    2260        ]
    2261
    2262        args_sql = ", ".join(self.sql(e) for e in args)
    2263        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2264        expressions = self.expressions(expression, flat=True)
    2265        expressions = f" BY {expressions}" if expressions else ""
    2266
    2267        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
    2268
    2269    def offset_sql(self, expression: exp.Offset) -> str:
    2270        this = self.sql(expression, "this")
    2271        value = expression.expression
    2272        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2273        expressions = self.expressions(expression, flat=True)
    2274        expressions = f" BY {expressions}" if expressions else ""
    2275        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
    2276
    2277    def setitem_sql(self, expression: exp.SetItem) -> str:
    2278        kind = self.sql(expression, "kind")
    2279        kind = f"{kind} " if kind else ""
    2280        this = self.sql(expression, "this")
    2281        expressions = self.expressions(expression)
    2282        collate = self.sql(expression, "collate")
    2283        collate = f" COLLATE {collate}" if collate else ""
    2284        global_ = "GLOBAL " if expression.args.get("global") else ""
    2285        return f"{global_}{kind}{this}{expressions}{collate}"
    2286
    2287    def set_sql(self, expression: exp.Set) -> str:
    2288        expressions = (
    2289            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    2290        )
    2291        tag = " TAG" if expression.args.get("tag") else ""
    2292        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
    2293
    2294    def pragma_sql(self, expression: exp.Pragma) -> str:
    2295        return f"PRAGMA {self.sql(expression, 'this')}"
    2296
    2297    def lock_sql(self, expression: exp.Lock) -> str:
    2298        if not self.LOCKING_READS_SUPPORTED:
    2299            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2300            return ""
    2301
    2302        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2303        expressions = self.expressions(expression, flat=True)
    2304        expressions = f" OF {expressions}" if expressions else ""
    2305        wait = expression.args.get("wait")
    2306
    2307        if wait is not None:
    2308            if isinstance(wait, exp.Literal):
    2309                wait = f" WAIT {self.sql(wait)}"
    2310            else:
    2311                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2312
    2313        return f"{lock_type}{expressions}{wait or ''}"
    2314
    2315    def literal_sql(self, expression: exp.Literal) -> str:
    2316        text = expression.this or ""
    2317        if expression.is_string:
    2318            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2319        return text
    2320
    2321    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2322        if self.dialect.ESCAPED_SEQUENCES:
    2323            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2324            text = "".join(
    2325                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2326            )
    2327
    2328        return self._replace_line_breaks(text).replace(
    2329            self.dialect.QUOTE_END, self._escaped_quote_end
    2330        )
    2331
    2332    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2333        local = " LOCAL" if expression.args.get("local") else ""
    2334        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2335        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2336        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2337        partition = self.sql(expression, "partition")
    2338        partition = f" {partition}" if partition else ""
    2339        input_format = self.sql(expression, "input_format")
    2340        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2341        serde = self.sql(expression, "serde")
    2342        serde = f" SERDE {serde}" if serde else ""
    2343        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
    2344
    2345    def null_sql(self, *_) -> str:
    2346        return "NULL"
    2347
    2348    def boolean_sql(self, expression: exp.Boolean) -> str:
    2349        return "TRUE" if expression.this else "FALSE"
    2350
    2351    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2352        this = self.sql(expression, "this")
    2353        this = f"{this} " if this else this
    2354        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2355        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2356
    2357    def withfill_sql(self, expression: exp.WithFill) -> str:
    2358        from_sql = self.sql(expression, "from")
    2359        from_sql = f" FROM {from_sql}" if from_sql else ""
    2360        to_sql = self.sql(expression, "to")
    2361        to_sql = f" TO {to_sql}" if to_sql else ""
    2362        step_sql = self.sql(expression, "step")
    2363        step_sql = f" STEP {step_sql}" if step_sql else ""
    2364        interpolated_values = [
    2365            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2366            if isinstance(e, exp.Alias)
    2367            else self.sql(e, "this")
    2368            for e in expression.args.get("interpolate") or []
    2369        ]
    2370        interpolate = (
    2371            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2372        )
    2373        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
    2374
    2375    def cluster_sql(self, expression: exp.Cluster) -> str:
    2376        return self.op_expressions("CLUSTER BY", expression)
    2377
    2378    def distribute_sql(self, expression: exp.Distribute) -> str:
    2379        return self.op_expressions("DISTRIBUTE BY", expression)
    2380
    2381    def sort_sql(self, expression: exp.Sort) -> str:
    2382        return self.op_expressions("SORT BY", expression)
    2383
    2384    def ordered_sql(self, expression: exp.Ordered) -> str:
    2385        desc = expression.args.get("desc")
    2386        asc = not desc
    2387
    2388        nulls_first = expression.args.get("nulls_first")
    2389        nulls_last = not nulls_first
    2390        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2391        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2392        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2393
    2394        this = self.sql(expression, "this")
    2395
    2396        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2397        nulls_sort_change = ""
    2398        if nulls_first and (
    2399            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2400        ):
    2401            nulls_sort_change = " NULLS FIRST"
    2402        elif (
    2403            nulls_last
    2404            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2405            and not nulls_are_last
    2406        ):
    2407            nulls_sort_change = " NULLS LAST"
    2408
    2409        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2410        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2411            window = expression.find_ancestor(exp.Window, exp.Select)
    2412            if isinstance(window, exp.Window) and window.args.get("spec"):
    2413                self.unsupported(
    2414                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2415                )
    2416                nulls_sort_change = ""
    2417            elif self.NULL_ORDERING_SUPPORTED is False and (
    2418                (asc and nulls_sort_change == " NULLS LAST")
    2419                or (desc and nulls_sort_change == " NULLS FIRST")
    2420            ):
    2421                # BigQuery does not allow these ordering/nulls combinations when used under
    2422                # an aggregation func or under a window containing one
    2423                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2424
    2425                if isinstance(ancestor, exp.Window):
    2426                    ancestor = ancestor.this
    2427                if isinstance(ancestor, exp.AggFunc):
    2428                    self.unsupported(
    2429                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2430                    )
    2431                    nulls_sort_change = ""
    2432            elif self.NULL_ORDERING_SUPPORTED is None:
    2433                if expression.this.is_int:
    2434                    self.unsupported(
    2435                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2436                    )
    2437                elif not isinstance(expression.this, exp.Rand):
    2438                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2439                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2440                nulls_sort_change = ""
    2441
    2442        with_fill = self.sql(expression, "with_fill")
    2443        with_fill = f" {with_fill}" if with_fill else ""
    2444
    2445        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
    2446
    2447    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2448        window_frame = self.sql(expression, "window_frame")
    2449        window_frame = f"{window_frame} " if window_frame else ""
    2450
    2451        this = self.sql(expression, "this")
    2452
    2453        return f"{window_frame}{this}"
    2454
    2455    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2456        partition = self.partition_by_sql(expression)
    2457        order = self.sql(expression, "order")
    2458        measures = self.expressions(expression, key="measures")
    2459        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2460        rows = self.sql(expression, "rows")
    2461        rows = self.seg(rows) if rows else ""
    2462        after = self.sql(expression, "after")
    2463        after = self.seg(after) if after else ""
    2464        pattern = self.sql(expression, "pattern")
    2465        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2466        definition_sqls = [
    2467            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2468            for definition in expression.args.get("define", [])
    2469        ]
    2470        definitions = self.expressions(sqls=definition_sqls)
    2471        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2472        body = "".join(
    2473            (
    2474                partition,
    2475                order,
    2476                measures,
    2477                rows,
    2478                after,
    2479                pattern,
    2480                define,
    2481            )
    2482        )
    2483        alias = self.sql(expression, "alias")
    2484        alias = f" {alias}" if alias else ""
    2485        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
    2486
    2487    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2488        limit = expression.args.get("limit")
    2489
    2490        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2491            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2492        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2493            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2494
    2495        return csv(
    2496            *sqls,
    2497            *[self.sql(join) for join in expression.args.get("joins") or []],
    2498            self.sql(expression, "match"),
    2499            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2500            self.sql(expression, "prewhere"),
    2501            self.sql(expression, "where"),
    2502            self.sql(expression, "connect"),
    2503            self.sql(expression, "group"),
    2504            self.sql(expression, "having"),
    2505            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2506            self.sql(expression, "order"),
    2507            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2508            *self.after_limit_modifiers(expression),
    2509            self.options_modifier(expression),
    2510            sep="",
    2511        )
    2512
    2513    def options_modifier(self, expression: exp.Expression) -> str:
    2514        options = self.expressions(expression, key="options")
    2515        return f" {options}" if options else ""
    2516
    2517    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2518        return ""
    2519
    2520    def offset_limit_modifiers(
    2521        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2522    ) -> t.List[str]:
    2523        return [
    2524            self.sql(expression, "offset") if fetch else self.sql(limit),
    2525            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2526        ]
    2527
    2528    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
    2529        locks = self.expressions(expression, key="locks", sep=" ")
    2530        locks = f" {locks}" if locks else ""
    2531        return [locks, self.sql(expression, "sample")]
    2532
    2533    def select_sql(self, expression: exp.Select) -> str:
    2534        into = expression.args.get("into")
    2535        if not self.SUPPORTS_SELECT_INTO and into:
    2536            into.pop()
    2537
    2538        hint = self.sql(expression, "hint")
    2539        distinct = self.sql(expression, "distinct")
    2540        distinct = f" {distinct}" if distinct else ""
    2541        kind = self.sql(expression, "kind")
    2542
    2543        limit = expression.args.get("limit")
    2544        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2545            top = self.limit_sql(limit, top=True)
    2546            limit.pop()
    2547        else:
    2548            top = ""
    2549
    2550        expressions = self.expressions(expression)
    2551
    2552        if kind:
    2553            if kind in self.SELECT_KINDS:
    2554                kind = f" AS {kind}"
    2555            else:
    2556                if kind == "STRUCT":
    2557                    expressions = self.expressions(
    2558                        sqls=[
    2559                            self.sql(
    2560                                exp.Struct(
    2561                                    expressions=[
    2562                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2563                                        if isinstance(e, exp.Alias)
    2564                                        else e
    2565                                        for e in expression.expressions
    2566                                    ]
    2567                                )
    2568                            )
    2569                        ]
    2570                    )
    2571                kind = ""
    2572
    2573        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2574        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2575
    2576        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2577        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2578        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2579        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2580        sql = self.query_modifiers(
    2581            expression,
    2582            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2583            self.sql(expression, "into", comment=False),
    2584            self.sql(expression, "from", comment=False),
    2585        )
    2586
    2587        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2588        if expression.args.get("with"):
    2589            sql = self.maybe_comment(sql, expression)
    2590            expression.pop_comments()
    2591
    2592        sql = self.prepend_ctes(expression, sql)
    2593
    2594        if not self.SUPPORTS_SELECT_INTO and into:
    2595            if into.args.get("temporary"):
    2596                table_kind = " TEMPORARY"
    2597            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2598                table_kind = " UNLOGGED"
    2599            else:
    2600                table_kind = ""
    2601            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2602
    2603        return sql
    2604
    2605    def schema_sql(self, expression: exp.Schema) -> str:
    2606        this = self.sql(expression, "this")
    2607        sql = self.schema_columns_sql(expression)
    2608        return f"{this} {sql}" if this and sql else this or sql
    2609
    2610    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2611        if expression.expressions:
    2612            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2613        return ""
    2614
    2615    def star_sql(self, expression: exp.Star) -> str:
    2616        except_ = self.expressions(expression, key="except", flat=True)
    2617        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2618        replace = self.expressions(expression, key="replace", flat=True)
    2619        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2620        rename = self.expressions(expression, key="rename", flat=True)
    2621        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2622        return f"*{except_}{replace}{rename}"
    2623
    2624    def parameter_sql(self, expression: exp.Parameter) -> str:
    2625        this = self.sql(expression, "this")
    2626        return f"{self.PARAMETER_TOKEN}{this}"
    2627
    2628    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2629        this = self.sql(expression, "this")
    2630        kind = expression.text("kind")
    2631        if kind:
    2632            kind = f"{kind}."
    2633        return f"@@{kind}{this}"
    2634
    2635    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2636        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"
    2637
    2638    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2639        alias = self.sql(expression, "alias")
    2640        alias = f"{sep}{alias}" if alias else ""
    2641        sample = self.sql(expression, "sample")
    2642        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2643            alias = f"{sample}{alias}"
    2644
    2645            # Set to None so it's not generated again by self.query_modifiers()
    2646            expression.set("sample", None)
    2647
    2648        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2649        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2650        return self.prepend_ctes(expression, sql)
    2651
    2652    def qualify_sql(self, expression: exp.Qualify) -> str:
    2653        this = self.indent(self.sql(expression, "this"))
    2654        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
    2655
    2656    def unnest_sql(self, expression: exp.Unnest) -> str:
    2657        args = self.expressions(expression, flat=True)
    2658
    2659        alias = expression.args.get("alias")
    2660        offset = expression.args.get("offset")
    2661
    2662        if self.UNNEST_WITH_ORDINALITY:
    2663            if alias and isinstance(offset, exp.Expression):
    2664                alias.append("columns", offset)
    2665
    2666        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2667            columns = alias.columns
    2668            alias = self.sql(columns[0]) if columns else ""
    2669        else:
    2670            alias = self.sql(alias)
    2671
    2672        alias = f" AS {alias}" if alias else alias
    2673        if self.UNNEST_WITH_ORDINALITY:
    2674            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2675        else:
    2676            if isinstance(offset, exp.Expression):
    2677                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2678            elif offset:
    2679                suffix = f"{alias} WITH OFFSET"
    2680            else:
    2681                suffix = alias
    2682
    2683        return f"UNNEST({args}){suffix}"
    2684
    2685    def prewhere_sql(self, expression: exp.PreWhere) -> str:
    2686        return ""
    2687
    2688    def where_sql(self, expression: exp.Where) -> str:
    2689        this = self.indent(self.sql(expression, "this"))
    2690        return f"{self.seg('WHERE')}{self.sep()}{this}"
    2691
    2692    def window_sql(self, expression: exp.Window) -> str:
    2693        this = self.sql(expression, "this")
    2694        partition = self.partition_by_sql(expression)
    2695        order = expression.args.get("order")
    2696        order = self.order_sql(order, flat=True) if order else ""
    2697        spec = self.sql(expression, "spec")
    2698        alias = self.sql(expression, "alias")
    2699        over = self.sql(expression, "over") or "OVER"
    2700
    2701        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2702
    2703        first = expression.args.get("first")
    2704        if first is None:
    2705            first = ""
    2706        else:
    2707            first = "FIRST" if first else "LAST"
    2708
    2709        if not partition and not order and not spec and alias:
    2710            return f"{this} {alias}"
    2711
    2712        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2713        return f"{this} ({args})"
    2714
    2715    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
    2716        partition = self.expressions(expression, key="partition_by", flat=True)
    2717        return f"PARTITION BY {partition}" if partition else ""
    2718
    2719    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2720        kind = self.sql(expression, "kind")
    2721        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2722        end = (
    2723            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2724            or "CURRENT ROW"
    2725        )
    2726        return f"{kind} BETWEEN {start} AND {end}"
    2727
    2728    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
    2729        this = self.sql(expression, "this")
    2730        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
    2731        return f"{this} WITHIN GROUP ({expression_sql})"
    2732
    2733    def between_sql(self, expression: exp.Between) -> str:
    2734        this = self.sql(expression, "this")
    2735        low = self.sql(expression, "low")
    2736        high = self.sql(expression, "high")
    2737        return f"{this} BETWEEN {low} AND {high}"
    2738
    2739    def bracket_offset_expressions(
    2740        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2741    ) -> t.List[exp.Expression]:
    2742        return apply_index_offset(
    2743            expression.this,
    2744            expression.expressions,
    2745            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2746        )
    2747
    2748    def bracket_sql(self, expression: exp.Bracket) -> str:
    2749        expressions = self.bracket_offset_expressions(expression)
    2750        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2751        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
    2752
    2753    def all_sql(self, expression: exp.All) -> str:
    2754        return f"ALL {self.wrap(expression)}"
    2755
    2756    def any_sql(self, expression: exp.Any) -> str:
    2757        this = self.sql(expression, "this")
    2758        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2759            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2760                this = self.wrap(this)
    2761            return f"ANY{this}"
    2762        return f"ANY {this}"
    2763
    2764    def exists_sql(self, expression: exp.Exists) -> str:
    2765        return f"EXISTS{self.wrap(expression)}"
    2766
    2767    def case_sql(self, expression: exp.Case) -> str:
    2768        this = self.sql(expression, "this")
    2769        statements = [f"CASE {this}" if this else "CASE"]
    2770
    2771        for e in expression.args["ifs"]:
    2772            statements.append(f"WHEN {self.sql(e, 'this')}")
    2773            statements.append(f"THEN {self.sql(e, 'true')}")
    2774
    2775        default = self.sql(expression, "default")
    2776
    2777        if default:
    2778            statements.append(f"ELSE {default}")
    2779
    2780        statements.append("END")
    2781
    2782        if self.pretty and self.too_wide(statements):
    2783            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2784
    2785        return " ".join(statements)
    2786
    2787    def constraint_sql(self, expression: exp.Constraint) -> str:
    2788        this = self.sql(expression, "this")
    2789        expressions = self.expressions(expression, flat=True)
    2790        return f"CONSTRAINT {this} {expressions}"
    2791
    2792    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
    2793        order = expression.args.get("order")
    2794        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
    2795        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
    2796
    2797    def extract_sql(self, expression: exp.Extract) -> str:
    2798        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2799        expression_sql = self.sql(expression, "expression")
    2800        return f"EXTRACT({this} FROM {expression_sql})"
    2801
    2802    def trim_sql(self, expression: exp.Trim) -> str:
    2803        trim_type = self.sql(expression, "position")
    2804
    2805        if trim_type == "LEADING":
    2806            func_name = "LTRIM"
    2807        elif trim_type == "TRAILING":
    2808            func_name = "RTRIM"
    2809        else:
    2810            func_name = "TRIM"
    2811
    2812        return self.func(func_name, expression.this, expression.expression)
    2813
    2814    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2815        args = expression.expressions
    2816        if isinstance(expression, exp.ConcatWs):
    2817            args = args[1:]  # Skip the delimiter
    2818
    2819        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2820            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2821
    2822        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2823            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2824
    2825        return args
    2826
    2827    def concat_sql(self, expression: exp.Concat) -> str:
    2828        expressions = self.convert_concat_args(expression)
    2829
    2830        # Some dialects don't allow a single-argument CONCAT call
    2831        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2832            return self.sql(expressions[0])
    2833
    2834        return self.func("CONCAT", *expressions)
    2835
    2836    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2837        return self.func(
    2838            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2839        )
    2840
    2841    def check_sql(self, expression: exp.Check) -> str:
    2842        this = self.sql(expression, key="this")
    2843        return f"CHECK ({this})"
    2844
    2845    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2846        expressions = self.expressions(expression, flat=True)
    2847        expressions = f" ({expressions})" if expressions else ""
    2848        reference = self.sql(expression, "reference")
    2849        reference = f" {reference}" if reference else ""
    2850        delete = self.sql(expression, "delete")
    2851        delete = f" ON DELETE {delete}" if delete else ""
    2852        update = self.sql(expression, "update")
    2853        update = f" ON UPDATE {update}" if update else ""
    2854        return f"FOREIGN KEY{expressions}{reference}{delete}{update}"
    2855
    2856    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2857        expressions = self.expressions(expression, flat=True)
    2858        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2859        options = f" {options}" if options else ""
    2860        return f"PRIMARY KEY ({expressions}){options}"
    2861
    2862    def if_sql(self, expression: exp.If) -> str:
    2863        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
    2864
    2865    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
    2866        modifier = expression.args.get("modifier")
    2867        modifier = f" {modifier}" if modifier else ""
    2868        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
    2869
    2870    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2871        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
    2872
    2873    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2874        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2875
    2876        if expression.args.get("escape"):
    2877            path = self.escape_str(path)
    2878
    2879        if self.QUOTE_JSON_PATH:
    2880            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2881
    2882        return path
    2883
    2884    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2885        if isinstance(expression, exp.JSONPathPart):
    2886            transform = self.TRANSFORMS.get(expression.__class__)
    2887            if not callable(transform):
    2888                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2889                return ""
    2890
    2891            return transform(self, expression)
    2892
    2893        if isinstance(expression, int):
    2894            return str(expression)
    2895
    2896        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2897            escaped = expression.replace("'", "\\'")
    2898            escaped = f"\\'{expression}\\'"
    2899        else:
    2900            escaped = expression.replace('"', '\\"')
    2901            escaped = f'"{escaped}"'
    2902
    2903        return escaped
    2904
    2905    def formatjson_sql(self, expression: exp.FormatJson) -> str:
    2906        return f"{self.sql(expression, 'this')} FORMAT JSON"
    2907
    2908    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2909        null_handling = expression.args.get("null_handling")
    2910        null_handling = f" {null_handling}" if null_handling else ""
    2911
    2912        unique_keys = expression.args.get("unique_keys")
    2913        if unique_keys is not None:
    2914            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2915        else:
    2916            unique_keys = ""
    2917
    2918        return_type = self.sql(expression, "return_type")
    2919        return_type = f" RETURNING {return_type}" if return_type else ""
    2920        encoding = self.sql(expression, "encoding")
    2921        encoding = f" ENCODING {encoding}" if encoding else ""
    2922
    2923        return self.func(
    2924            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2925            *expression.expressions,
    2926            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2927        )
    2928
    2929    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
    2930        return self.jsonobject_sql(expression)
    2931
    2932    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2933        null_handling = expression.args.get("null_handling")
    2934        null_handling = f" {null_handling}" if null_handling else ""
    2935        return_type = self.sql(expression, "return_type")
    2936        return_type = f" RETURNING {return_type}" if return_type else ""
    2937        strict = " STRICT" if expression.args.get("strict") else ""
    2938        return self.func(
    2939            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2940        )
    2941
    2942    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2943        this = self.sql(expression, "this")
    2944        order = self.sql(expression, "order")
    2945        null_handling = expression.args.get("null_handling")
    2946        null_handling = f" {null_handling}" if null_handling else ""
    2947        return_type = self.sql(expression, "return_type")
    2948        return_type = f" RETURNING {return_type}" if return_type else ""
    2949        strict = " STRICT" if expression.args.get("strict") else ""
    2950        return self.func(
    2951            "JSON_ARRAYAGG",
    2952            this,
    2953            suffix=f"{order}{null_handling}{return_type}{strict})",
    2954        )
    2955
    2956    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2957        path = self.sql(expression, "path")
    2958        path = f" PATH {path}" if path else ""
    2959        nested_schema = self.sql(expression, "nested_schema")
    2960
    2961        if nested_schema:
    2962            return f"NESTED{path} {nested_schema}"
    2963
    2964        this = self.sql(expression, "this")
    2965        kind = self.sql(expression, "kind")
    2966        kind = f" {kind}" if kind else ""
    2967        return f"{this}{kind}{path}"
    2968
    2969    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
    2970        return self.func("COLUMNS", *expression.expressions)
    2971
    2972    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2973        this = self.sql(expression, "this")
    2974        path = self.sql(expression, "path")
    2975        path = f", {path}" if path else ""
    2976        error_handling = expression.args.get("error_handling")
    2977        error_handling = f" {error_handling}" if error_handling else ""
    2978        empty_handling = expression.args.get("empty_handling")
    2979        empty_handling = f" {empty_handling}" if empty_handling else ""
    2980        schema = self.sql(expression, "schema")
    2981        return self.func(
    2982            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2983        )
    2984
    2985    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2986        this = self.sql(expression, "this")
    2987        kind = self.sql(expression, "kind")
    2988        path = self.sql(expression, "path")
    2989        path = f" {path}" if path else ""
    2990        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2991        return f"{this} {kind}{path}{as_json}"
    2992
    2993    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2994        this = self.sql(expression, "this")
    2995        path = self.sql(expression, "path")
    2996        path = f", {path}" if path else ""
    2997        expressions = self.expressions(expression)
    2998        with_ = (
    2999            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    3000            if expressions
    3001            else ""
    3002        )
    3003        return f"OPENJSON({this}{path}){with_}"
    3004
    3005    def in_sql(self, expression: exp.In) -> str:
    3006        query = expression.args.get("query")
    3007        unnest = expression.args.get("unnest")
    3008        field = expression.args.get("field")
    3009        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3010
    3011        if query:
    3012            in_sql = self.sql(query)
    3013        elif unnest:
    3014            in_sql = self.in_unnest_op(unnest)
    3015        elif field:
    3016            in_sql = self.sql(field)
    3017        else:
    3018            in_sql = f"({self.expressions(expression, flat=True)})"
    3019
    3020        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
    3021
    3022    def in_unnest_op(self, unnest: exp.Unnest) -> str:
    3023        return f"(SELECT {self.sql(unnest)})"
    3024
    3025    def interval_sql(self, expression: exp.Interval) -> str:
    3026        unit = self.sql(expression, "unit")
    3027        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3028            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3029        unit = f" {unit}" if unit else ""
    3030
    3031        if self.SINGLE_STRING_INTERVAL:
    3032            this = expression.this.name if expression.this else ""
    3033            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    3034
    3035        this = self.sql(expression, "this")
    3036        if this:
    3037            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3038            this = f" {this}" if unwrapped else f" ({this})"
    3039
    3040        return f"INTERVAL{this}{unit}"
    3041
    3042    def return_sql(self, expression: exp.Return) -> str:
    3043        return f"RETURN {self.sql(expression, 'this')}"
    3044
    3045    def reference_sql(self, expression: exp.Reference) -> str:
    3046        this = self.sql(expression, "this")
    3047        expressions = self.expressions(expression, flat=True)
    3048        expressions = f"({expressions})" if expressions else ""
    3049        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3050        options = f" {options}" if options else ""
    3051        return f"REFERENCES {this}{expressions}{options}"
    3052
    3053    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3054        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3055        parent = expression.parent
    3056        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3057        return self.func(
    3058            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3059        )
    3060
    3061    def paren_sql(self, expression: exp.Paren) -> str:
    3062        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    3063        return f"({sql}{self.seg(')', sep='')}"
    3064
    3065    def neg_sql(self, expression: exp.Neg) -> str:
    3066        # This makes sure we don't convert "- - 5" to "--5", which is a comment
    3067        this_sql = self.sql(expression, "this")
    3068        sep = " " if this_sql[0] == "-" else ""
    3069        return f"-{sep}{this_sql}"
    3070
    3071    def not_sql(self, expression: exp.Not) -> str:
    3072        return f"NOT {self.sql(expression, 'this')}"
    3073
    3074    def alias_sql(self, expression: exp.Alias) -> str:
    3075        alias = self.sql(expression, "alias")
    3076        alias = f" AS {alias}" if alias else ""
    3077        return f"{self.sql(expression, 'this')}{alias}"
    3078
    3079    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3080        alias = expression.args["alias"]
    3081
    3082        identifier_alias = isinstance(alias, exp.Identifier)
    3083        literal_alias = isinstance(alias, exp.Literal)
    3084
    3085        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3086            alias.replace(exp.Literal.string(alias.output_name))
    3087        elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3088            alias.replace(exp.to_identifier(alias.output_name))
    3089
    3090        return self.alias_sql(expression)
    3091
    3092    def aliases_sql(self, expression: exp.Aliases) -> str:
    3093        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
    3094
    3095    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3096        this = self.sql(expression, "this")
    3097        index = self.sql(expression, "expression")
    3098        return f"{this} AT {index}"
    3099
    3100    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3101        this = self.sql(expression, "this")
    3102        zone = self.sql(expression, "zone")
    3103        return f"{this} AT TIME ZONE {zone}"
    3104
    3105    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3106        this = self.sql(expression, "this")
    3107        zone = self.sql(expression, "zone")
    3108        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
    3109
    3110    def add_sql(self, expression: exp.Add) -> str:
    3111        return self.binary(expression, "+")
    3112
    3113    def and_sql(
    3114        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3115    ) -> str:
    3116        return self.connector_sql(expression, "AND", stack)
    3117
    3118    def or_sql(
    3119        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3120    ) -> str:
    3121        return self.connector_sql(expression, "OR", stack)
    3122
    3123    def xor_sql(
    3124        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3125    ) -> str:
    3126        return self.connector_sql(expression, "XOR", stack)
    3127
    3128    def connector_sql(
    3129        self,
    3130        expression: exp.Connector,
    3131        op: str,
    3132        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3133    ) -> str:
    3134        if stack is not None:
    3135            if expression.expressions:
    3136                stack.append(self.expressions(expression, sep=f" {op} "))
    3137            else:
    3138                stack.append(expression.right)
    3139                if expression.comments and self.comments:
    3140                    for comment in expression.comments:
    3141                        if comment:
    3142                            op += f" /*{self.pad_comment(comment)}*/"
    3143                stack.extend((op, expression.left))
    3144            return op
    3145
    3146        stack = [expression]
    3147        sqls: t.List[str] = []
    3148        ops = set()
    3149
    3150        while stack:
    3151            node = stack.pop()
    3152            if isinstance(node, exp.Connector):
    3153                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3154            else:
    3155                sql = self.sql(node)
    3156                if sqls and sqls[-1] in ops:
    3157                    sqls[-1] += f" {sql}"
    3158                else:
    3159                    sqls.append(sql)
    3160
    3161        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3162        return sep.join(sqls)
    3163
    3164    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
    3165        return self.binary(expression, "&")
    3166
    3167    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
    3168        return self.binary(expression, "<<")
    3169
    3170    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
    3171        return f"~{self.sql(expression, 'this')}"
    3172
    3173    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
    3174        return self.binary(expression, "|")
    3175
    3176    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
    3177        return self.binary(expression, ">>")
    3178
    3179    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
    3180        return self.binary(expression, "^")
    3181
    3182    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3183        format_sql = self.sql(expression, "format")
    3184        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3185        to_sql = self.sql(expression, "to")
    3186        to_sql = f" {to_sql}" if to_sql else ""
    3187        action = self.sql(expression, "action")
    3188        action = f" {action}" if action else ""
    3189        default = self.sql(expression, "default")
    3190        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3191        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"
    3192
    3193    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
    3194        zone = self.sql(expression, "this")
    3195        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
    3196
    3197    def collate_sql(self, expression: exp.Collate) -> str:
    3198        if self.COLLATE_IS_FUNC:
    3199            return self.function_fallback_sql(expression)
    3200        return self.binary(expression, "COLLATE")
    3201
    3202    def command_sql(self, expression: exp.Command) -> str:
    3203        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
    3204
    3205    def comment_sql(self, expression: exp.Comment) -> str:
    3206        this = self.sql(expression, "this")
    3207        kind = expression.args["kind"]
    3208        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3209        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3210        expression_sql = self.sql(expression, "expression")
    3211        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
    3212
    3213    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3214        this = self.sql(expression, "this")
    3215        delete = " DELETE" if expression.args.get("delete") else ""
    3216        recompress = self.sql(expression, "recompress")
    3217        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3218        to_disk = self.sql(expression, "to_disk")
    3219        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3220        to_volume = self.sql(expression, "to_volume")
    3221        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3222        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
    3223
    3224    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3225        where = self.sql(expression, "where")
    3226        group = self.sql(expression, "group")
    3227        aggregates = self.expressions(expression, key="aggregates")
    3228        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3229
    3230        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3231            return f"TTL {self.expressions(expression, flat=True)}"
    3232
    3233        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
    3234
    3235    def transaction_sql(self, expression: exp.Transaction) -> str:
    3236        return "BEGIN"
    3237
    3238    def commit_sql(self, expression: exp.Commit) -> str:
    3239        chain = expression.args.get("chain")
    3240        if chain is not None:
    3241            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3242
    3243        return f"COMMIT{chain or ''}"
    3244
    3245    def rollback_sql(self, expression: exp.Rollback) -> str:
    3246        savepoint = expression.args.get("savepoint")
    3247        savepoint = f" TO {savepoint}" if savepoint else ""
    3248        return f"ROLLBACK{savepoint}"
    3249
    3250    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3251        this = self.sql(expression, "this")
    3252
    3253        dtype = self.sql(expression, "dtype")
    3254        if dtype:
    3255            collate = self.sql(expression, "collate")
    3256            collate = f" COLLATE {collate}" if collate else ""
    3257            using = self.sql(expression, "using")
    3258            using = f" USING {using}" if using else ""
    3259            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
    3260
    3261        default = self.sql(expression, "default")
    3262        if default:
    3263            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3264
    3265        comment = self.sql(expression, "comment")
    3266        if comment:
    3267            return f"ALTER COLUMN {this} COMMENT {comment}"
    3268
    3269        allow_null = expression.args.get("allow_null")
    3270        drop = expression.args.get("drop")
    3271
    3272        if not drop and not allow_null:
    3273            self.unsupported("Unsupported ALTER COLUMN syntax")
    3274
    3275        if allow_null is not None:
    3276            keyword = "DROP" if drop else "SET"
    3277            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3278
    3279        return f"ALTER COLUMN {this} DROP DEFAULT"
    3280
    3281    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3282        this = self.sql(expression, "this")
    3283        if not isinstance(expression.this, exp.Var):
    3284            this = f"KEY DISTKEY {this}"
    3285        return f"ALTER DISTSTYLE {this}"
    3286
    3287    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3288        compound = " COMPOUND" if expression.args.get("compound") else ""
    3289        this = self.sql(expression, "this")
    3290        expressions = self.expressions(expression, flat=True)
    3291        expressions = f"({expressions})" if expressions else ""
    3292        return f"ALTER{compound} SORTKEY {this or expressions}"
    3293
    3294    def alterrename_sql(self, expression: exp.AlterRename) -> str:
    3295        if not self.RENAME_TABLE_WITH_DB:
    3296            # Remove db from tables
    3297            expression = expression.transform(
    3298                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3299            ).assert_is(exp.AlterRename)
    3300        this = self.sql(expression, "this")
    3301        return f"RENAME TO {this}"
    3302
    3303    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3304        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3305        old_column = self.sql(expression, "this")
    3306        new_column = self.sql(expression, "to")
    3307        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
    3308
    3309    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3310        exprs = self.expressions(expression, flat=True)
    3311        return f"SET {exprs}"
    3312
    3313    def alter_sql(self, expression: exp.Alter) -> str:
    3314        actions = expression.args["actions"]
    3315
    3316        if isinstance(actions[0], exp.ColumnDef):
    3317            actions = self.add_column_sql(expression)
    3318        elif isinstance(actions[0], exp.Schema):
    3319            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3320        elif isinstance(actions[0], exp.Delete):
    3321            actions = self.expressions(expression, key="actions", flat=True)
    3322        elif isinstance(actions[0], exp.Query):
    3323            actions = "AS " + self.expressions(expression, key="actions")
    3324        else:
    3325            actions = self.expressions(expression, key="actions", flat=True)
    3326
    3327        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3328        on_cluster = self.sql(expression, "cluster")
    3329        on_cluster = f" {on_cluster}" if on_cluster else ""
    3330        only = " ONLY" if expression.args.get("only") else ""
    3331        options = self.expressions(expression, key="options")
    3332        options = f", {options}" if options else ""
    3333        kind = self.sql(expression, "kind")
    3334        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3335
    3336        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"
    3337
    3338    def add_column_sql(self, expression: exp.Alter) -> str:
    3339        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3340            return self.expressions(
    3341                expression,
    3342                key="actions",
    3343                prefix="ADD COLUMN ",
    3344                skip_first=True,
    3345            )
    3346        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
    3347
    3348    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3349        expressions = self.expressions(expression)
    3350        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3351        return f"DROP{exists}{expressions}"
    3352
    3353    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3354        return f"ADD {self.expressions(expression)}"
    3355
    3356    def distinct_sql(self, expression: exp.Distinct) -> str:
    3357        this = self.expressions(expression, flat=True)
    3358
    3359        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3360            case = exp.case()
    3361            for arg in expression.expressions:
    3362                case = case.when(arg.is_(exp.null()), exp.null())
    3363            this = self.sql(case.else_(f"({this})"))
    3364
    3365        this = f" {this}" if this else ""
    3366
    3367        on = self.sql(expression, "on")
    3368        on = f" ON {on}" if on else ""
    3369        return f"DISTINCT{this}{on}"
    3370
    3371    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    3372        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
    3373
    3374    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    3375        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
    3376
    3377    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3378        this_sql = self.sql(expression, "this")
    3379        expression_sql = self.sql(expression, "expression")
    3380        kind = "MAX" if expression.args.get("max") else "MIN"
    3381        return f"{this_sql} HAVING {kind} {expression_sql}"
    3382
    3383    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3384        return self.sql(
    3385            exp.Cast(
    3386                this=exp.Div(this=expression.this, expression=expression.expression),
    3387                to=exp.DataType(this=exp.DataType.Type.INT),
    3388            )
    3389        )
    3390
    3391    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3392        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3393            return self.func(
    3394                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3395            )
    3396        return self.binary(expression, "||")
    3397
    3398    def div_sql(self, expression: exp.Div) -> str:
    3399        l, r = expression.left, expression.right
    3400
    3401        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3402            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3403
    3404        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3405            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3406                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3407
    3408        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3409            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3410                return self.sql(
    3411                    exp.cast(
    3412                        l / r,
    3413                        to=exp.DataType.Type.BIGINT,
    3414                    )
    3415                )
    3416
    3417        return self.binary(expression, "/")
    3418
    3419    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3420        n = exp._wrap(expression.this, exp.Binary)
    3421        d = exp._wrap(expression.expression, exp.Binary)
    3422        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))
    3423
    3424    def overlaps_sql(self, expression: exp.Overlaps) -> str:
    3425        return self.binary(expression, "OVERLAPS")
    3426
    3427    def distance_sql(self, expression: exp.Distance) -> str:
    3428        return self.binary(expression, "<->")
    3429
    3430    def dot_sql(self, expression: exp.Dot) -> str:
    3431        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
    3432
    3433    def eq_sql(self, expression: exp.EQ) -> str:
    3434        return self.binary(expression, "=")
    3435
    3436    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
    3437        return self.binary(expression, ":=")
    3438
    3439    def escape_sql(self, expression: exp.Escape) -> str:
    3440        return self.binary(expression, "ESCAPE")
    3441
    3442    def glob_sql(self, expression: exp.Glob) -> str:
    3443        return self.binary(expression, "GLOB")
    3444
    3445    def gt_sql(self, expression: exp.GT) -> str:
    3446        return self.binary(expression, ">")
    3447
    3448    def gte_sql(self, expression: exp.GTE) -> str:
    3449        return self.binary(expression, ">=")
    3450
    3451    def ilike_sql(self, expression: exp.ILike) -> str:
    3452        return self.binary(expression, "ILIKE")
    3453
    3454    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
    3455        return self.binary(expression, "ILIKE ANY")
    3456
    3457    def is_sql(self, expression: exp.Is) -> str:
    3458        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3459            return self.sql(
    3460                expression.this if expression.expression.this else exp.not_(expression.this)
    3461            )
    3462        return self.binary(expression, "IS")
    3463
    3464    def like_sql(self, expression: exp.Like) -> str:
    3465        return self.binary(expression, "LIKE")
    3466
    3467    def likeany_sql(self, expression: exp.LikeAny) -> str:
    3468        return self.binary(expression, "LIKE ANY")
    3469
    3470    def similarto_sql(self, expression: exp.SimilarTo) -> str:
    3471        return self.binary(expression, "SIMILAR TO")
    3472
    3473    def lt_sql(self, expression: exp.LT) -> str:
    3474        return self.binary(expression, "<")
    3475
    3476    def lte_sql(self, expression: exp.LTE) -> str:
    3477        return self.binary(expression, "<=")
    3478
    3479    def mod_sql(self, expression: exp.Mod) -> str:
    3480        return self.binary(expression, "%")
    3481
    3482    def mul_sql(self, expression: exp.Mul) -> str:
    3483        return self.binary(expression, "*")
    3484
    3485    def neq_sql(self, expression: exp.NEQ) -> str:
    3486        return self.binary(expression, "<>")
    3487
    3488    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
    3489        return self.binary(expression, "IS NOT DISTINCT FROM")
    3490
    3491    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
    3492        return self.binary(expression, "IS DISTINCT FROM")
    3493
    3494    def slice_sql(self, expression: exp.Slice) -> str:
    3495        return self.binary(expression, ":")
    3496
    3497    def sub_sql(self, expression: exp.Sub) -> str:
    3498        return self.binary(expression, "-")
    3499
    3500    def trycast_sql(self, expression: exp.TryCast) -> str:
    3501        return self.cast_sql(expression, safe_prefix="TRY_")
    3502
    3503    def try_sql(self, expression: exp.Try) -> str:
    3504        if not self.TRY_SUPPORTED:
    3505            self.unsupported("Unsupported TRY function")
    3506            return self.sql(expression, "this")
    3507
    3508        return self.func("TRY", expression.this)
    3509
    3510    def log_sql(self, expression: exp.Log) -> str:
    3511        this = expression.this
    3512        expr = expression.expression
    3513
    3514        if self.dialect.LOG_BASE_FIRST is False:
    3515            this, expr = expr, this
    3516        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3517            if this.name in ("2", "10"):
    3518                return self.func(f"LOG{this.name}", expr)
    3519
    3520            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3521
    3522        return self.func("LOG", this, expr)
    3523
    3524    def use_sql(self, expression: exp.Use) -> str:
    3525        kind = self.sql(expression, "kind")
    3526        kind = f" {kind}" if kind else ""
    3527        this = self.sql(expression, "this")
    3528        this = f" {this}" if this else ""
    3529        return f"USE{kind}{this}"
    3530
    3531    def binary(self, expression: exp.Binary, op: str) -> str:
    3532        sqls: t.List[str] = []
    3533        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3534        binary_type = type(expression)
    3535
    3536        while stack:
    3537            node = stack.pop()
    3538
    3539            if type(node) is binary_type:
    3540                op_func = node.args.get("operator")
    3541                if op_func:
    3542                    op = f"OPERATOR({self.sql(op_func)})"
    3543
    3544                stack.append(node.right)
    3545                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3546                stack.append(node.left)
    3547            else:
    3548                sqls.append(self.sql(node))
    3549
    3550        return "".join(sqls)
    3551
    3552    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3553        to_clause = self.sql(expression, "to")
    3554        if to_clause:
    3555            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3556
    3557        return self.function_fallback_sql(expression)
    3558
    3559    def function_fallback_sql(self, expression: exp.Func) -> str:
    3560        args = []
    3561
    3562        for key in expression.arg_types:
    3563            arg_value = expression.args.get(key)
    3564
    3565            if isinstance(arg_value, list):
    3566                for value in arg_value:
    3567                    args.append(value)
    3568            elif arg_value is not None:
    3569                args.append(arg_value)
    3570
    3571        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3572            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3573        else:
    3574            name = expression.sql_name()
    3575
    3576        return self.func(name, *args)
    3577
    3578    def func(
    3579        self,
    3580        name: str,
    3581        *args: t.Optional[exp.Expression | str],
    3582        prefix: str = "(",
    3583        suffix: str = ")",
    3584        normalize: bool = True,
    3585    ) -> str:
    3586        name = self.normalize_func(name) if normalize else name
    3587        return f"{name}{prefix}{self.format_args(*args)}{suffix}"
    3588
    3589    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3590        arg_sqls = tuple(
    3591            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3592        )
    3593        if self.pretty and self.too_wide(arg_sqls):
    3594            return self.indent(
    3595                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3596            )
    3597        return sep.join(arg_sqls)
    3598
    3599    def too_wide(self, args: t.Iterable) -> bool:
    3600        return sum(len(arg) for arg in args) > self.max_text_width
    3601
    3602    def format_time(
    3603        self,
    3604        expression: exp.Expression,
    3605        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3606        inverse_time_trie: t.Optional[t.Dict] = None,
    3607    ) -> t.Optional[str]:
    3608        return format_time(
    3609            self.sql(expression, "format"),
    3610            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3611            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3612        )
    3613
    3614    def expressions(
    3615        self,
    3616        expression: t.Optional[exp.Expression] = None,
    3617        key: t.Optional[str] = None,
    3618        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3619        flat: bool = False,
    3620        indent: bool = True,
    3621        skip_first: bool = False,
    3622        skip_last: bool = False,
    3623        sep: str = ", ",
    3624        prefix: str = "",
    3625        dynamic: bool = False,
    3626        new_line: bool = False,
    3627    ) -> str:
    3628        expressions = expression.args.get(key or "expressions") if expression else sqls
    3629
    3630        if not expressions:
    3631            return ""
    3632
    3633        if flat:
    3634            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3635
    3636        num_sqls = len(expressions)
    3637        result_sqls = []
    3638
    3639        for i, e in enumerate(expressions):
    3640            sql = self.sql(e, comment=False)
    3641            if not sql:
    3642                continue
    3643
    3644            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3645
    3646            if self.pretty:
    3647                if self.leading_comma:
    3648                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3649                else:
    3650                    result_sqls.append(
    3651                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3652                    )
    3653            else:
    3654                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3655
    3656        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3657            if new_line:
    3658                result_sqls.insert(0, "")
    3659                result_sqls.append("")
    3660            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3661        else:
    3662            result_sql = "".join(result_sqls)
    3663
    3664        return (
    3665            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3666            if indent
    3667            else result_sql
    3668        )
    3669
    3670    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3671        flat = flat or isinstance(expression.parent, exp.Properties)
    3672        expressions_sql = self.expressions(expression, flat=flat)
    3673        if flat:
    3674            return f"{op} {expressions_sql}"
    3675        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
    3676
    3677    def naked_property(self, expression: exp.Property) -> str:
    3678        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3679        if not property_name:
    3680            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3681        return f"{property_name} {self.sql(expression, 'this')}"
    3682
    3683    def tag_sql(self, expression: exp.Tag) -> str:
    3684        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
    3685
    3686    def token_sql(self, token_type: TokenType) -> str:
    3687        return self.TOKEN_MAPPING.get(token_type, token_type.name)
    3688
    3689    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3690        this = self.sql(expression, "this")
    3691        expressions = self.no_identify(self.expressions, expression)
    3692        expressions = (
    3693            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3694        )
    3695        return f"{this}{expressions}" if expressions.strip() != "" else this
    3696
    3697    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3698        this = self.sql(expression, "this")
    3699        expressions = self.expressions(expression, flat=True)
    3700        return f"{this}({expressions})"
    3701
    3702    def kwarg_sql(self, expression: exp.Kwarg) -> str:
    3703        return self.binary(expression, "=>")
    3704
    3705    def when_sql(self, expression: exp.When) -> str:
    3706        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3707        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3708        condition = self.sql(expression, "condition")
    3709        condition = f" AND {condition}" if condition else ""
    3710
    3711        then_expression = expression.args.get("then")
    3712        if isinstance(then_expression, exp.Insert):
    3713            this = self.sql(then_expression, "this")
    3714            this = f"INSERT {this}" if this else "INSERT"
    3715            then = self.sql(then_expression, "expression")
    3716            then = f"{this} VALUES {then}" if then else this
    3717        elif isinstance(then_expression, exp.Update):
    3718            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3719                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3720            else:
    3721                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3722        else:
    3723            then = self.sql(then_expression)
    3724        return f"WHEN {matched}{source}{condition} THEN {then}"
    3725
    3726    def whens_sql(self, expression: exp.Whens) -> str:
    3727        return self.expressions(expression, sep=" ", indent=False)
    3728
    3729    def merge_sql(self, expression: exp.Merge) -> str:
    3730        table = expression.this
    3731        table_alias = ""
    3732
    3733        hints = table.args.get("hints")
    3734        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3735            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3736            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3737
    3738        this = self.sql(table)
    3739        using = f"USING {self.sql(expression, 'using')}"
    3740        on = f"ON {self.sql(expression, 'on')}"
    3741        whens = self.sql(expression, "whens")
    3742
    3743        returning = self.sql(expression, "returning")
    3744        if returning:
    3745            whens = f"{whens}{returning}"
    3746
    3747        sep = self.sep()
    3748
    3749        return self.prepend_ctes(
    3750            expression,
    3751            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    3752        )
    3753
    3754    @unsupported_args("format")
    3755    def tochar_sql(self, expression: exp.ToChar) -> str:
    3756        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))
    3757
    3758    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3759        if not self.SUPPORTS_TO_NUMBER:
    3760            self.unsupported("Unsupported TO_NUMBER function")
    3761            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3762
    3763        fmt = expression.args.get("format")
    3764        if not fmt:
    3765            self.unsupported("Conversion format is required for TO_NUMBER")
    3766            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3767
    3768        return self.func("TO_NUMBER", expression.this, fmt)
    3769
    3770    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3771        this = self.sql(expression, "this")
    3772        kind = self.sql(expression, "kind")
    3773        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3774        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3775        return f"{this}({kind}{args})"
    3776
    3777    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3778        this = self.sql(expression, "this")
    3779        max = self.sql(expression, "max")
    3780        min = self.sql(expression, "min")
    3781        return f"{this}(MIN {min} MAX {max})"
    3782
    3783    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
    3784        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
    3785
    3786    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
    3787        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"
    3788
    3789    # https://docs.starrocks.io/docs/sql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE/
    3790    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
    3791        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"
    3792
    3793    # https://docs.starrocks.io/docs/sql-reference/sql-statements/data-definition/CREATE_TABLE/#distribution_desc
    3794    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    3795        expressions = self.expressions(expression, flat=True)
    3796        expressions = f" {self.wrap(expressions)}" if expressions else ""
    3797        buckets = self.sql(expression, "buckets")
    3798        kind = self.sql(expression, "kind")
    3799        buckets = f" BUCKETS {buckets}" if buckets else ""
    3800        order = self.sql(expression, "order")
    3801        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"
    3802
    3803    def oncluster_sql(self, expression: exp.OnCluster) -> str:
    3804        return ""
    3805
    3806    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3807        expressions = self.expressions(expression, key="expressions", flat=True)
    3808        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3809        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3810        buckets = self.sql(expression, "buckets")
    3811        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
    3812
    3813    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3814        this = self.sql(expression, "this")
    3815        having = self.sql(expression, "having")
    3816
    3817        if having:
    3818            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3819
    3820        return self.func("ANY_VALUE", this)
    3821
    3822    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3823        transform = self.func("TRANSFORM", *expression.expressions)
    3824        row_format_before = self.sql(expression, "row_format_before")
    3825        row_format_before = f" {row_format_before}" if row_format_before else ""
    3826        record_writer = self.sql(expression, "record_writer")
    3827        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3828        using = f" USING {self.sql(expression, 'command_script')}"
    3829        schema = self.sql(expression, "schema")
    3830        schema = f" AS {schema}" if schema else ""
    3831        row_format_after = self.sql(expression, "row_format_after")
    3832        row_format_after = f" {row_format_after}" if row_format_after else ""
    3833        record_reader = self.sql(expression, "record_reader")
    3834        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3835        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
    3836
    3837    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3838        key_block_size = self.sql(expression, "key_block_size")
    3839        if key_block_size:
    3840            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3841
    3842        using = self.sql(expression, "using")
    3843        if using:
    3844            return f"USING {using}"
    3845
    3846        parser = self.sql(expression, "parser")
    3847        if parser:
    3848            return f"WITH PARSER {parser}"
    3849
    3850        comment = self.sql(expression, "comment")
    3851        if comment:
    3852            return f"COMMENT {comment}"
    3853
    3854        visible = expression.args.get("visible")
    3855        if visible is not None:
    3856            return "VISIBLE" if visible else "INVISIBLE"
    3857
    3858        engine_attr = self.sql(expression, "engine_attr")
    3859        if engine_attr:
    3860            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3861
    3862        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3863        if secondary_engine_attr:
    3864            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3865
    3866        self.unsupported("Unsupported index constraint option.")
    3867        return ""
    3868
    3869    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
    3870        enforced = " ENFORCED" if expression.args.get("enforced") else ""
    3871        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
    3872
    3873    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3874        kind = self.sql(expression, "kind")
    3875        kind = f"{kind} INDEX" if kind else "INDEX"
    3876        this = self.sql(expression, "this")
    3877        this = f" {this}" if this else ""
    3878        index_type = self.sql(expression, "index_type")
    3879        index_type = f" USING {index_type}" if index_type else ""
    3880        expressions = self.expressions(expression, flat=True)
    3881        expressions = f" ({expressions})" if expressions else ""
    3882        options = self.expressions(expression, key="options", sep=" ")
    3883        options = f" {options}" if options else ""
    3884        return f"{kind}{this}{index_type}{expressions}{options}"
    3885
    3886    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3887        if self.NVL2_SUPPORTED:
    3888            return self.function_fallback_sql(expression)
    3889
    3890        case = exp.Case().when(
    3891            expression.this.is_(exp.null()).not_(copy=False),
    3892            expression.args["true"],
    3893            copy=False,
    3894        )
    3895        else_cond = expression.args.get("false")
    3896        if else_cond:
    3897            case.else_(else_cond, copy=False)
    3898
    3899        return self.sql(case)
    3900
    3901    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3902        this = self.sql(expression, "this")
    3903        expr = self.sql(expression, "expression")
    3904        iterator = self.sql(expression, "iterator")
    3905        condition = self.sql(expression, "condition")
    3906        condition = f" IF {condition}" if condition else ""
    3907        return f"{this} FOR {expr} IN {iterator}{condition}"
    3908
    3909    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
    3910        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
    3911
    3912    def opclass_sql(self, expression: exp.Opclass) -> str:
    3913        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
    3914
    3915    def predict_sql(self, expression: exp.Predict) -> str:
    3916        model = self.sql(expression, "this")
    3917        model = f"MODEL {model}"
    3918        table = self.sql(expression, "expression")
    3919        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3920        parameters = self.sql(expression, "params_struct")
    3921        return self.func("PREDICT", model, table, parameters or None)
    3922
    3923    def forin_sql(self, expression: exp.ForIn) -> str:
    3924        this = self.sql(expression, "this")
    3925        expression_sql = self.sql(expression, "expression")
    3926        return f"FOR {this} DO {expression_sql}"
    3927
    3928    def refresh_sql(self, expression: exp.Refresh) -> str:
    3929        this = self.sql(expression, "this")
    3930        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3931        return f"REFRESH {table}{this}"
    3932
    3933    def toarray_sql(self, expression: exp.ToArray) -> str:
    3934        arg = expression.this
    3935        if not arg.type:
    3936            from sqlglot.optimizer.annotate_types import annotate_types
    3937
    3938            arg = annotate_types(arg)
    3939
    3940        if arg.is_type(exp.DataType.Type.ARRAY):
    3941            return self.sql(arg)
    3942
    3943        cond_for_null = arg.is_(exp.null())
    3944        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
    3945
    3946    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3947        this = expression.this
    3948        time_format = self.format_time(expression)
    3949
    3950        if time_format:
    3951            return self.sql(
    3952                exp.cast(
    3953                    exp.StrToTime(this=this, format=expression.args["format"]),
    3954                    exp.DataType.Type.TIME,
    3955                )
    3956            )
    3957
    3958        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3959            return self.sql(this)
    3960
    3961        return self.sql(exp.cast(this, exp.DataType.Type.TIME))
    3962
    3963    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3964        this = expression.this
    3965        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3966            return self.sql(this)
    3967
    3968        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))
    3969
    3970    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    3971        this = expression.this
    3972        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    3973            return self.sql(this)
    3974
    3975        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))
    3976
    3977    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3978        this = expression.this
    3979        time_format = self.format_time(expression)
    3980
    3981        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3982            return self.sql(
    3983                exp.cast(
    3984                    exp.StrToTime(this=this, format=expression.args["format"]),
    3985                    exp.DataType.Type.DATE,
    3986                )
    3987            )
    3988
    3989        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3990            return self.sql(this)
    3991
    3992        return self.sql(exp.cast(this, exp.DataType.Type.DATE))
    3993
    3994    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3995        return self.sql(
    3996            exp.func(
    3997                "DATEDIFF",
    3998                expression.this,
    3999                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    4000                "day",
    4001            )
    4002        )
    4003
    4004    def lastday_sql(self, expression: exp.LastDay) -> str:
    4005        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4006            return self.function_fallback_sql(expression)
    4007
    4008        unit = expression.text("unit")
    4009        if unit and unit != "MONTH":
    4010            self.unsupported("Date parts are not supported in LAST_DAY.")
    4011
    4012        return self.func("LAST_DAY", expression.this)
    4013
    4014    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4015        from sqlglot.dialects.dialect import unit_to_str
    4016
    4017        return self.func(
    4018            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4019        )
    4020
    4021    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4022        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4023            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4024            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4025            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4026            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4027
    4028        from sqlglot.dialects import Dialect
    4029
    4030        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4031        if self.dialect.__class__ != Dialect:
    4032            self.unsupported("ARRAY_ANY is unsupported")
    4033
    4034        return self.function_fallback_sql(expression)
    4035
    4036    def struct_sql(self, expression: exp.Struct) -> str:
    4037        expression.set(
    4038            "expressions",
    4039            [
    4040                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4041                if isinstance(e, exp.PropertyEQ)
    4042                else e
    4043                for e in expression.expressions
    4044            ],
    4045        )
    4046
    4047        return self.function_fallback_sql(expression)
    4048
    4049    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4050        low = self.sql(expression, "this")
    4051        high = self.sql(expression, "expression")
    4052
    4053        return f"{low} TO {high}"
    4054
    4055    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4056        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4057        tables = f" {self.expressions(expression)}"
    4058
    4059        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4060
    4061        on_cluster = self.sql(expression, "cluster")
    4062        on_cluster = f" {on_cluster}" if on_cluster else ""
    4063
    4064        identity = self.sql(expression, "identity")
    4065        identity = f" {identity} IDENTITY" if identity else ""
    4066
    4067        option = self.sql(expression, "option")
    4068        option = f" {option}" if option else ""
    4069
    4070        partition = self.sql(expression, "partition")
    4071        partition = f" {partition}" if partition else ""
    4072
    4073        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
    4074
    4075    # This transpiles T-SQL's CONVERT function
    4076    # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16
    4077    def convert_sql(self, expression: exp.Convert) -> str:
    4078        to = expression.this
    4079        value = expression.expression
    4080        style = expression.args.get("style")
    4081        safe = expression.args.get("safe")
    4082        strict = expression.args.get("strict")
    4083
    4084        if not to or not value:
    4085            return ""
    4086
    4087        # Retrieve length of datatype and override to default if not specified
    4088        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4089            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4090
    4091        transformed: t.Optional[exp.Expression] = None
    4092        cast = exp.Cast if strict else exp.TryCast
    4093
    4094        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4095        if isinstance(style, exp.Literal) and style.is_int:
    4096            from sqlglot.dialects.tsql import TSQL
    4097
    4098            style_value = style.name
    4099            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4100            if not converted_style:
    4101                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4102
    4103            fmt = exp.Literal.string(converted_style)
    4104
    4105            if to.this == exp.DataType.Type.DATE:
    4106                transformed = exp.StrToDate(this=value, format=fmt)
    4107            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4108                transformed = exp.StrToTime(this=value, format=fmt)
    4109            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4110                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4111            elif to.this == exp.DataType.Type.TEXT:
    4112                transformed = exp.TimeToStr(this=value, format=fmt)
    4113
    4114        if not transformed:
    4115            transformed = cast(this=value, to=to, safe=safe)
    4116
    4117        return self.sql(transformed)
    4118
    4119    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
    4120        this = expression.this
    4121        if isinstance(this, exp.JSONPathWildcard):
    4122            this = self.json_path_part(this)
    4123            return f".{this}" if this else ""
    4124
    4125        if exp.SAFE_IDENTIFIER_RE.match(this):
    4126            return f".{this}"
    4127
    4128        this = self.json_path_part(this)
    4129        return (
    4130            f"[{this}]"
    4131            if self._quote_json_path_key_using_brackets and self.JSON_PATH_BRACKETED_KEY_SUPPORTED
    4132            else f".{this}"
    4133        )
    4134
    4135    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
    4136        this = self.json_path_part(expression.this)
    4137        return f"[{this}]" if this else ""
    4138
    4139    def _simplify_unless_literal(self, expression: E) -> E:
    4140        if not isinstance(expression, exp.Literal):
    4141            from sqlglot.optimizer.simplify import simplify
    4142
    4143            expression = simplify(expression, dialect=self.dialect)
    4144
    4145        return expression
    4146
    4147    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
    4148        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
    4149            # The first modifier here will be the one closest to the AggFunc's arg
    4150            mods = sorted(
    4151                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
    4152                key=lambda x: 0
    4153                if isinstance(x, exp.HavingMax)
    4154                else (1 if isinstance(x, exp.Order) else 2),
    4155            )
    4156
    4157            if mods:
    4158                mod = mods[0]
    4159                this = expression.__class__(this=mod.this.copy())
    4160                this.meta["inline"] = True
    4161                mod.this.replace(this)
    4162                return self.sql(expression.this)
    4163
    4164            agg_func = expression.find(exp.AggFunc)
    4165
    4166            if agg_func:
    4167                return self.sql(agg_func)[:-1] + f" {text})"
    4168
    4169        return f"{self.sql(expression, 'this')} {text}"
    4170
    4171    def _replace_line_breaks(self, string: str) -> str:
    4172        """We don't want to extra indent line breaks so we temporarily replace them with sentinels."""
    4173        if self.pretty:
    4174            return string.replace("\n", self.SENTINEL_LINE_BREAK)
    4175        return string
    4176
    4177    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4178        option = self.sql(expression, "this")
    4179
    4180        if expression.expressions:
    4181            upper = option.upper()
    4182
    4183            # Snowflake FILE_FORMAT options are separated by whitespace
    4184            sep = " " if upper == "FILE_FORMAT" else ", "
    4185
    4186            # Databricks copy/format options do not set their list of values with EQ
    4187            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4188            values = self.expressions(expression, flat=True, sep=sep)
    4189            return f"{option}{op}({values})"
    4190
    4191        value = self.sql(expression, "expression")
    4192
    4193        if not value:
    4194            return option
    4195
    4196        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4197
    4198        return f"{option}{op}{value}"
    4199
    4200    def credentials_sql(self, expression: exp.Credentials) -> str:
    4201        cred_expr = expression.args.get("credentials")
    4202        if isinstance(cred_expr, exp.Literal):
    4203            # Redshift case: CREDENTIALS <string>
    4204            credentials = self.sql(expression, "credentials")
    4205            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4206        else:
    4207            # Snowflake case: CREDENTIALS = (...)
    4208            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4209            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4210
    4211        storage = self.sql(expression, "storage")
    4212        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4213
    4214        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4215        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4216
    4217        iam_role = self.sql(expression, "iam_role")
    4218        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4219
    4220        region = self.sql(expression, "region")
    4221        region = f" REGION {region}" if region else ""
    4222
    4223        return f"{credentials}{storage}{encryption}{iam_role}{region}"
    4224
    4225    def copy_sql(self, expression: exp.Copy) -> str:
    4226        this = self.sql(expression, "this")
    4227        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4228
    4229        credentials = self.sql(expression, "credentials")
    4230        credentials = self.seg(credentials) if credentials else ""
    4231        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    4232        files = self.expressions(expression, key="files", flat=True)
    4233
    4234        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4235        params = self.expressions(
    4236            expression,
    4237            key="params",
    4238            sep=sep,
    4239            new_line=True,
    4240            skip_last=True,
    4241            skip_first=True,
    4242            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4243        )
    4244
    4245        if params:
    4246            if self.COPY_PARAMS_ARE_WRAPPED:
    4247                params = f" WITH ({params})"
    4248            elif not self.pretty:
    4249                params = f" {params}"
    4250
    4251        return f"COPY{this}{kind} {files}{credentials}{params}"
    4252
    4253    def semicolon_sql(self, expression: exp.Semicolon) -> str:
    4254        return ""
    4255
    4256    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4257        on_sql = "ON" if expression.args.get("on") else "OFF"
    4258        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4259        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4260        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4261        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4262
    4263        if filter_col or retention_period:
    4264            on_sql = self.func("ON", filter_col, retention_period)
    4265
    4266        return f"DATA_DELETION={on_sql}"
    4267
    4268    def maskingpolicycolumnconstraint_sql(
    4269        self, expression: exp.MaskingPolicyColumnConstraint
    4270    ) -> str:
    4271        this = self.sql(expression, "this")
    4272        expressions = self.expressions(expression, flat=True)
    4273        expressions = f" USING ({expressions})" if expressions else ""
    4274        return f"MASKING POLICY {this}{expressions}"
    4275
    4276    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4277        this = self.sql(expression, "this")
    4278        this = f"TABLE {this}"
    4279        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])
    4280
    4281    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    4282        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)
    4283
    4284    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4285        this = self.sql(expression, "this")
    4286        expr = expression.expression
    4287
    4288        if isinstance(expr, exp.Func):
    4289            # T-SQL's CLR functions are case sensitive
    4290            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4291        else:
    4292            expr = self.sql(expression, "expression")
    4293
    4294        return self.scope_resolution(expr, this)
    4295
    4296    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4297        if self.PARSE_JSON_NAME is None:
    4298            return self.sql(expression.this)
    4299
    4300        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)
    4301
    4302    def rand_sql(self, expression: exp.Rand) -> str:
    4303        lower = self.sql(expression, "lower")
    4304        upper = self.sql(expression, "upper")
    4305
    4306        if lower and upper:
    4307            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4308        return self.func("RAND", expression.this)
    4309
    4310    def changes_sql(self, expression: exp.Changes) -> str:
    4311        information = self.sql(expression, "information")
    4312        information = f"INFORMATION => {information}"
    4313        at_before = self.sql(expression, "at_before")
    4314        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4315        end = self.sql(expression, "end")
    4316        end = f"{self.seg('')}{end}" if end else ""
    4317
    4318        return f"CHANGES ({information}){at_before}{end}"
    4319
    4320    def pad_sql(self, expression: exp.Pad) -> str:
    4321        prefix = "L" if expression.args.get("is_left") else "R"
    4322
    4323        fill_pattern = self.sql(expression, "fill_pattern") or None
    4324        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4325            fill_pattern = "' '"
    4326
    4327        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)
    4328
    4329    def summarize_sql(self, expression: exp.Summarize) -> str:
    4330        table = " TABLE" if expression.args.get("table") else ""
    4331        return f"SUMMARIZE{table} {self.sql(expression.this)}"
    4332
    4333    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4334        generate_series = exp.GenerateSeries(**expression.args)
    4335
    4336        parent = expression.parent
    4337        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4338            parent = parent.parent
    4339
    4340        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4341            return self.sql(exp.Unnest(expressions=[generate_series]))
    4342
    4343        if isinstance(parent, exp.Select):
    4344            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4345
    4346        return self.sql(generate_series)
    4347
    4348    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4349        exprs = expression.expressions
    4350        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4351            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4352        else:
    4353            rhs = self.expressions(expression)
    4354
    4355        return self.func(name, expression.this, rhs or None)
    4356
    4357    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4358        if self.SUPPORTS_CONVERT_TIMEZONE:
    4359            return self.function_fallback_sql(expression)
    4360
    4361        source_tz = expression.args.get("source_tz")
    4362        target_tz = expression.args.get("target_tz")
    4363        timestamp = expression.args.get("timestamp")
    4364
    4365        if source_tz and timestamp:
    4366            timestamp = exp.AtTimeZone(
    4367                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4368            )
    4369
    4370        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4371
    4372        return self.sql(expr)
    4373
    4374    def json_sql(self, expression: exp.JSON) -> str:
    4375        this = self.sql(expression, "this")
    4376        this = f" {this}" if this else ""
    4377
    4378        _with = expression.args.get("with")
    4379
    4380        if _with is None:
    4381            with_sql = ""
    4382        elif not _with:
    4383            with_sql = " WITHOUT"
    4384        else:
    4385            with_sql = " WITH"
    4386
    4387        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4388
    4389        return f"JSON{this}{with_sql}{unique_sql}"
    4390
    4391    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4392        def _generate_on_options(arg: t.Any) -> str:
    4393            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4394
    4395        path = self.sql(expression, "path")
    4396        returning = self.sql(expression, "returning")
    4397        returning = f" RETURNING {returning}" if returning else ""
    4398
    4399        on_condition = self.sql(expression, "on_condition")
    4400        on_condition = f" {on_condition}" if on_condition else ""
    4401
    4402        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")
    4403
    4404    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4405        else_ = "ELSE " if expression.args.get("else_") else ""
    4406        condition = self.sql(expression, "expression")
    4407        condition = f"WHEN {condition} THEN " if condition else else_
    4408        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4409        return f"{condition}{insert}"
    4410
    4411    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4412        kind = self.sql(expression, "kind")
    4413        expressions = self.seg(self.expressions(expression, sep=" "))
    4414        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4415        return res
    4416
    4417    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4418        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4419        empty = expression.args.get("empty")
    4420        empty = (
    4421            f"DEFAULT {empty} ON EMPTY"
    4422            if isinstance(empty, exp.Expression)
    4423            else self.sql(expression, "empty")
    4424        )
    4425
    4426        error = expression.args.get("error")
    4427        error = (
    4428            f"DEFAULT {error} ON ERROR"
    4429            if isinstance(error, exp.Expression)
    4430            else self.sql(expression, "error")
    4431        )
    4432
    4433        if error and empty:
    4434            error = (
    4435                f"{empty} {error}"
    4436                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4437                else f"{error} {empty}"
    4438            )
    4439            empty = ""
    4440
    4441        null = self.sql(expression, "null")
    4442
    4443        return f"{empty}{error}{null}"
    4444
    4445    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
    4446        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
    4447        return f"{self.sql(expression, 'option')} QUOTES{scalar}"
    4448
    4449    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4450        this = self.sql(expression, "this")
    4451        path = self.sql(expression, "path")
    4452
    4453        passing = self.expressions(expression, "passing")
    4454        passing = f" PASSING {passing}" if passing else ""
    4455
    4456        on_condition = self.sql(expression, "on_condition")
    4457        on_condition = f" {on_condition}" if on_condition else ""
    4458
    4459        path = f"{path}{passing}{on_condition}"
    4460
    4461        return self.func("JSON_EXISTS", this, path)
    4462
    4463    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4464        array_agg = self.function_fallback_sql(expression)
    4465
    4466        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4467        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4468        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4469            parent = expression.parent
    4470            if isinstance(parent, exp.Filter):
    4471                parent_cond = parent.expression.this
    4472                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4473            else:
    4474                this = expression.this
    4475                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4476                if this.find(exp.Column):
    4477                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4478                    this_sql = (
    4479                        self.expressions(this)
    4480                        if isinstance(this, exp.Distinct)
    4481                        else self.sql(expression, "this")
    4482                    )
    4483
    4484                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4485
    4486        return array_agg
    4487
    4488    def apply_sql(self, expression: exp.Apply) -> str:
    4489        this = self.sql(expression, "this")
    4490        expr = self.sql(expression, "expression")
    4491
    4492        return f"{this} APPLY({expr})"
    4493
    4494    def grant_sql(self, expression: exp.Grant) -> str:
    4495        privileges_sql = self.expressions(expression, key="privileges", flat=True)
    4496
    4497        kind = self.sql(expression, "kind")
    4498        kind = f" {kind}" if kind else ""
    4499
    4500        securable = self.sql(expression, "securable")
    4501        securable = f" {securable}" if securable else ""
    4502
    4503        principals = self.expressions(expression, key="principals", flat=True)
    4504
    4505        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
    4506
    4507        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"
    4508
    4509    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4510        this = self.sql(expression, "this")
    4511        columns = self.expressions(expression, flat=True)
    4512        columns = f"({columns})" if columns else ""
    4513
    4514        return f"{this}{columns}"
    4515
    4516    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4517        this = self.sql(expression, "this")
    4518
    4519        kind = self.sql(expression, "kind")
    4520        kind = f"{kind} " if kind else ""
    4521
    4522        return f"{kind}{this}"
    4523
    4524    def columns_sql(self, expression: exp.Columns):
    4525        func = self.function_fallback_sql(expression)
    4526        if expression.args.get("unpack"):
    4527            func = f"*{func}"
    4528
    4529        return func
    4530
    4531    def overlay_sql(self, expression: exp.Overlay):
    4532        this = self.sql(expression, "this")
    4533        expr = self.sql(expression, "expression")
    4534        from_sql = self.sql(expression, "from")
    4535        for_sql = self.sql(expression, "for")
    4536        for_sql = f" FOR {for_sql}" if for_sql else ""
    4537
    4538        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"
    4539
    4540    @unsupported_args("format")
    4541    def todouble_sql(self, expression: exp.ToDouble) -> str:
    4542        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    4543
    4544    def string_sql(self, expression: exp.String) -> str:
    4545        this = expression.this
    4546        zone = expression.args.get("zone")
    4547
    4548        if zone:
    4549            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4550            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4551            # set for source_tz to transpile the time conversion before the STRING cast
    4552            this = exp.ConvertTimezone(
    4553                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4554            )
    4555
    4556        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))
    4557
    4558    def median_sql(self, expression: exp.Median):
    4559        if not self.SUPPORTS_MEDIAN:
    4560            return self.sql(
    4561                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4562            )
    4563
    4564        return self.function_fallback_sql(expression)
    4565
    4566    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4567        filler = self.sql(expression, "this")
    4568        filler = f" {filler}" if filler else ""
    4569        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4570        return f"TRUNCATE{filler} {with_count}"
    4571
    4572    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4573        if self.SUPPORTS_UNIX_SECONDS:
    4574            return self.function_fallback_sql(expression)
    4575
    4576        start_ts = exp.cast(
    4577            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4578        )
    4579
    4580        return self.sql(
    4581            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    4582        )
    4583
    4584    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    4585        dim = expression.expression
    4586
    4587        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    4588        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    4589            if not (dim.is_int and dim.name == "1"):
    4590                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    4591            dim = None
    4592
    4593        # If dimension is required but not specified, default initialize it
    4594        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    4595            dim = exp.Literal.number(1)
    4596
    4597        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)
    4598
    4599    def attach_sql(self, expression: exp.Attach) -> str:
    4600        this = self.sql(expression, "this")
    4601        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    4602        expressions = self.expressions(expression)
    4603        expressions = f" ({expressions})" if expressions else ""
    4604
    4605        return f"ATTACH{exists_sql} {this}{expressions}"
    4606
    4607    def detach_sql(self, expression: exp.Detach) -> str:
    4608        this = self.sql(expression, "this")
    4609        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    4610
    4611        return f"DETACH{exists_sql} {this}"
    4612
    4613    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    4614        this = self.sql(expression, "this")
    4615        value = self.sql(expression, "expression")
    4616        value = f" {value}" if value else ""
    4617        return f"{this}{value}"
    4618
    4619    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4620        this_sql = self.sql(expression, "this")
    4621        if isinstance(expression.this, exp.Table):
    4622            this_sql = f"TABLE {this_sql}"
    4623
    4624        return self.func(
    4625            "FEATURES_AT_TIME",
    4626            this_sql,
    4627            expression.args.get("time"),
    4628            expression.args.get("num_rows"),
    4629            expression.args.get("ignore_feature_nulls"),
    4630        )
    4631
    4632    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    4633        return (
    4634            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    4635        )
    4636
    4637    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    4638        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    4639        encode = f"{encode} {self.sql(expression, 'this')}"
    4640
    4641        properties = expression.args.get("properties")
    4642        if properties:
    4643            encode = f"{encode} {self.properties(properties)}"
    4644
    4645        return encode
    4646
    4647    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    4648        this = self.sql(expression, "this")
    4649        include = f"INCLUDE {this}"
    4650
    4651        column_def = self.sql(expression, "column_def")
    4652        if column_def:
    4653            include = f"{include} {column_def}"
    4654
    4655        alias = self.sql(expression, "alias")
    4656        if alias:
    4657            include = f"{include} AS {alias}"
    4658
    4659        return include
    4660
    4661    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
    4662        name = f"NAME {self.sql(expression, 'this')}"
    4663        return self.func("XMLELEMENT", name, *expression.expressions)
    4664
    4665    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    4666        partitions = self.expressions(expression, "partition_expressions")
    4667        create = self.expressions(expression, "create_expressions")
    4668        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"
    4669
    4670    def partitionbyrangepropertydynamic_sql(
    4671        self, expression: exp.PartitionByRangePropertyDynamic
    4672    ) -> str:
    4673        start = self.sql(expression, "start")
    4674        end = self.sql(expression, "end")
    4675
    4676        every = expression.args["every"]
    4677        if isinstance(every, exp.Interval) and every.this.is_string:
    4678            every.this.replace(exp.Literal.number(every.name))
    4679
    4680        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"
    4681
    4682    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    4683        name = self.sql(expression, "this")
    4684        values = self.expressions(expression, flat=True)
    4685
    4686        return f"NAME {name} VALUE {values}"
    4687
    4688    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    4689        kind = self.sql(expression, "kind")
    4690        sample = self.sql(expression, "sample")
    4691        return f"SAMPLE {sample} {kind}"
    4692
    4693    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    4694        kind = self.sql(expression, "kind")
    4695        option = self.sql(expression, "option")
    4696        option = f" {option}" if option else ""
    4697        this = self.sql(expression, "this")
    4698        this = f" {this}" if this else ""
    4699        columns = self.expressions(expression)
    4700        columns = f" {columns}" if columns else ""
    4701        return f"{kind}{option} STATISTICS{this}{columns}"
    4702
    4703    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    4704        this = self.sql(expression, "this")
    4705        columns = self.expressions(expression)
    4706        inner_expression = self.sql(expression, "expression")
    4707        inner_expression = f" {inner_expression}" if inner_expression else ""
    4708        update_options = self.sql(expression, "update_options")
    4709        update_options = f" {update_options} UPDATE" if update_options else ""
    4710        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
    4711
    4712    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    4713        kind = self.sql(expression, "kind")
    4714        kind = f" {kind}" if kind else ""
    4715        return f"DELETE{kind} STATISTICS"
    4716
    4717    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
    4718        inner_expression = self.sql(expression, "expression")
    4719        return f"LIST CHAINED ROWS{inner_expression}"
    4720
    4721    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    4722        kind = self.sql(expression, "kind")
    4723        this = self.sql(expression, "this")
    4724        this = f" {this}" if this else ""
    4725        inner_expression = self.sql(expression, "expression")
    4726        return f"VALIDATE {kind}{this}{inner_expression}"
    4727
    4728    def analyze_sql(self, expression: exp.Analyze) -> str:
    4729        options = self.expressions(expression, key="options", sep=" ")
    4730        options = f" {options}" if options else ""
    4731        kind = self.sql(expression, "kind")
    4732        kind = f" {kind}" if kind else ""
    4733        this = self.sql(expression, "this")
    4734        this = f" {this}" if this else ""
    4735        mode = self.sql(expression, "mode")
    4736        mode = f" {mode}" if mode else ""
    4737        properties = self.sql(expression, "properties")
    4738        properties = f" {properties}" if properties else ""
    4739        partition = self.sql(expression, "partition")
    4740        partition = f" {partition}" if partition else ""
    4741        inner_expression = self.sql(expression, "expression")
    4742        inner_expression = f" {inner_expression}" if inner_expression else ""
    4743        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
    4744
    4745    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    4746        this = self.sql(expression, "this")
    4747        namespaces = self.expressions(expression, key="namespaces")
    4748        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    4749        passing = self.expressions(expression, key="passing")
    4750        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    4751        columns = self.expressions(expression, key="columns")
    4752        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    4753        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    4754        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"
    4755
    4756    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
    4757        this = self.sql(expression, "this")
    4758        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"
    4759
    4760    def export_sql(self, expression: exp.Export) -> str:
    4761        this = self.sql(expression, "this")
    4762        connection = self.sql(expression, "connection")
    4763        connection = f"WITH CONNECTION {connection} " if connection else ""
    4764        options = self.sql(expression, "options")
    4765        return f"EXPORT DATA {connection}{options} AS {this}"

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

###### Arguments:

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

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

    675    def __init__(
    676        self,
    677        pretty: t.Optional[bool] = None,
    678        identify: str | bool = False,
    679        normalize: bool = False,
    680        pad: int = 2,
    681        indent: int = 2,
    682        normalize_functions: t.Optional[str | bool] = None,
    683        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    684        max_unsupported: int = 3,
    685        leading_comma: bool = False,
    686        max_text_width: int = 80,
    687        comments: bool = True,
    688        dialect: DialectType = None,
    689    ):
    690        import sqlglot
    691        from sqlglot.dialects import Dialect
    692
    693        self.pretty = pretty if pretty is not None else sqlglot.pretty
    694        self.identify = identify
    695        self.normalize = normalize
    696        self.pad = pad
    697        self._indent = indent
    698        self.unsupported_level = unsupported_level
    699        self.max_unsupported = max_unsupported
    700        self.leading_comma = leading_comma
    701        self.max_text_width = max_text_width
    702        self.comments = comments
    703        self.dialect = Dialect.get_or_raise(dialect)
    704
    705        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    706        self.normalize_functions = (
    707            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    708        )
    709
    710        self.unsupported_messages: t.List[str] = []
    711        self._escaped_quote_end: str = (
    712            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    713        )
    714        self._escaped_identifier_end = self.dialect.IDENTIFIER_END * 2
    715
    716        self._next_name = name_sequence("_t")
    717
    718        self._identifier_start = self.dialect.IDENTIFIER_START
    719        self._identifier_end = self.dialect.IDENTIFIER_END
    720
    721        self._quote_json_path_key_using_brackets = True

TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeColumns'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AnalyzeWith'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayContainsAll'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ArrayOverlaps'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Ceil'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConnectByRoot'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EmptyProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Except'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Floor'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Intersect'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Int64'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Operator'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PivotAny'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Stream'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StreamingTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SwapTable'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Tags'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Union'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UsingData'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Uuid'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithProcedureOptions'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE = True
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathSubscript'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SET_OP_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
SUPPORTS_UESCAPE = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
QUOTE_JSON_PATH = True
PAD_FILL_PATTERN_IS_REQUIRED = False
SUPPORTS_EXPLODING_PROJECTIONS = True
ARRAY_CONCAT_IS_VAR_LEN = True
SUPPORTS_CONVERT_TIMEZONE = False
SUPPORTS_MEDIAN = True
SUPPORTS_UNIX_SECONDS = False
PARSE_JSON_NAME: Optional[str] = 'PARSE_JSON'
ARRAY_SIZE_NAME: str = 'ARRAY_LENGTH'
ALTER_SET_TYPE = 'SET DATA TYPE'
ARRAY_SIZE_DIM_REQUIRED: Optional[bool] = None
TYPE_MAPPING = {<Type.DATETIME2: 'DATETIME2'>: 'TIMESTAMP', <Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY', <Type.SMALLDATETIME: 'SMALLDATETIME'>: 'TIMESTAMP'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
EXPRESSION_PRECEDES_PROPERTIES_CREATABLES: Set[str] = set()
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistributedByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DuplicateKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DynamicProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EmptyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EncodeProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.IncludeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SecurityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StreamingTableProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StrictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Tags'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithProcedureOptions'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Command'>, <class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Describe'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.MultitableInserts'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.SetOperation'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.NVARCHAR: 'NVARCHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.CHAR: 'CHAR'>, <Type.VARCHAR: 'VARCHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str: View Source

    723    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
    724        """
    725        Generates the SQL string corresponding to the given syntax tree.
    726
    727        Args:
    728            expression: The syntax tree.
    729            copy: Whether to copy the expression. The generator performs mutations so
    730                it is safer to copy.
    731
    732        Returns:
    733            The SQL string corresponding to `expression`.
    734        """
    735        if copy:
    736            expression = expression.copy()
    737
    738        expression = self.preprocess(expression)
    739
    740        self.unsupported_messages = []
    741        sql = self.sql(expression).strip()
    742
    743        if self.pretty:
    744            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    745
    746        if self.unsupported_level == ErrorLevel.IGNORE:
    747            return sql
    748
    749        if self.unsupported_level == ErrorLevel.WARN:
    750            for msg in self.unsupported_messages:
    751                logger.warning(msg)
    752        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    753            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    754
    755        return sql

Generates the SQL string corresponding to the given syntax tree.

###### Arguments:

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

###### Returns:

> The SQL string corresponding to `expression`.

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

    757    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    758        """Apply generic preprocessing transformations to a given expression."""
    759        expression = self._move_ctes_to_top_level(expression)
    760
    761        if self.ENSURE_BOOLS:
    762            from sqlglot.transforms import ensure_bools
    763
    764            expression = ensure_bools(expression)
    765
    766        return expression

Apply generic preprocessing transformations to a given expression.

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

    779    def unsupported(self, message: str) -> None:
    780        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    781            raise UnsupportedError(message)
    782        self.unsupported_messages.append(message)

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

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

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

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

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

    790    def pad_comment(self, comment: str) -> str:
    791        comment = " " + comment if comment[0].strip() else comment
    792        comment = comment + " " if comment[-1].strip() else comment
    793        return comment

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

    795    def maybe_comment(
    796        self,
    797        sql: str,
    798        expression: t.Optional[exp.Expression] = None,
    799        comments: t.Optional[t.List[str]] = None,
    800        separated: bool = False,
    801    ) -> str:
    802        comments = (
    803            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    804            if self.comments
    805            else None
    806        )
    807
    808        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    809            return sql
    810
    811        comments_sql = " ".join(
    812            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    813        )
    814
    815        if not comments_sql:
    816            return sql
    817
    818        comments_sql = self._replace_line_breaks(comments_sql)
    819
    820        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    821            return (
    822                f"{self.sep()}{comments_sql}{sql}"
    823                if not sql or sql[0].isspace()
    824                else f"{comments_sql}{self.sep()}{sql}"
    825            )
    826
    827        return f"{sql} {comments_sql}"

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

    829    def wrap(self, expression: exp.Expression | str) -> str:
    830        this_sql = (
    831            self.sql(expression)
    832            if isinstance(expression, exp.UNWRAPPED_QUERIES)
    833            else self.sql(expression, "this")
    834        )
    835        if not this_sql:
    836            return "()"
    837
    838        this_sql = self.indent(this_sql, level=1, pad=0)
    839        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    841    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    842        original = self.identify
    843        self.identify = False
    844        result = func(*args, **kwargs)
    845        self.identify = original
    846        return result

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

    848    def normalize_func(self, name: str) -> str:
    849        if self.normalize_functions == "upper" or self.normalize_functions is True:
    850            return name.upper()
    851        if self.normalize_functions == "lower":
    852            return name.lower()
    853        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

    855    def indent(
    856        self,
    857        sql: str,
    858        level: int = 0,
    859        pad: t.Optional[int] = None,
    860        skip_first: bool = False,
    861        skip_last: bool = False,
    862    ) -> str:
    863        if not self.pretty or not sql:
    864            return sql
    865
    866        pad = self.pad if pad is None else pad
    867        lines = sql.split("\n")
    868
    869        return "\n".join(
    870            (
    871                line
    872                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    873                else f"{' ' * (level * self._indent + pad)}{line}"
    874            )
    875            for i, line in enumerate(lines)
    876        )

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

    878    def sql(
    879        self,
    880        expression: t.Optional[str | exp.Expression],
    881        key: t.Optional[str] = None,
    882        comment: bool = True,
    883    ) -> str:
    884        if not expression:
    885            return ""
    886
    887        if isinstance(expression, str):
    888            return expression
    889
    890        if key:
    891            value = expression.args.get(key)
    892            if value:
    893                return self.sql(value)
    894            return ""
    895
    896        transform = self.TRANSFORMS.get(expression.__class__)
    897
    898        if callable(transform):
    899            sql = transform(self, expression)
    900        elif isinstance(expression, exp.Expression):
    901            exp_handler_name = f"{expression.key}_sql"
    902
    903            if hasattr(self, exp_handler_name):
    904                sql = getattr(self, exp_handler_name)(expression)
    905            elif isinstance(expression, exp.Func):
    906                sql = self.function_fallback_sql(expression)
    907            elif isinstance(expression, exp.Property):
    908                sql = self.property_sql(expression)
    909            else:
    910                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    911        else:
    912            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    913
    914        return self.maybe_comment(sql, expression) if self.comments and comment else sql

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

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

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

    921    def cache_sql(self, expression: exp.Cache) -> str:
    922        lazy = " LAZY" if expression.args.get("lazy") else ""
    923        table = self.sql(expression, "this")
    924        options = expression.args.get("options")
    925        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    926        sql = self.sql(expression, "expression")
    927        sql = f" AS{self.sep()}{sql}" if sql else ""
    928        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    929        return self.prepend_ctes(expression, sql)

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

    931    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    932        if isinstance(expression.parent, exp.Cast):
    933            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    934        default = "DEFAULT " if expression.args.get("default") else ""
    935        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    937    def column_parts(self, expression: exp.Column) -> str:
    938        return ".".join(
    939            self.sql(part)
    940            for part in (
    941                expression.args.get("catalog"),
    942                expression.args.get("db"),
    943                expression.args.get("table"),
    944                expression.args.get("this"),
    945            )
    946            if part
    947        )

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

    949    def column_sql(self, expression: exp.Column) -> str:
    950        join_mark = " (+)" if expression.args.get("join_mark") else ""
    951
    952        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
    953            join_mark = ""
    954            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    955
    956        return f"{self.column_parts(expression)}{join_mark}"

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

    958    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    959        this = self.sql(expression, "this")
    960        this = f" {this}" if this else ""
    961        position = self.sql(expression, "position")
    962        return f"{position}{this}"

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

    964    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    965        column = self.sql(expression, "this")
    966        kind = self.sql(expression, "kind")
    967        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    968        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    969        kind = f"{sep}{kind}" if kind else ""
    970        constraints = f" {constraints}" if constraints else ""
    971        position = self.sql(expression, "position")
    972        position = f" {position}" if position else ""
    973
    974        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    975            kind = ""
    976
    977        return f"{exists}{column}{kind}{constraints}{position}"

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

    979    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    980        this = self.sql(expression, "this")
    981        kind_sql = self.sql(expression, "kind").strip()
    982        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    984    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    985        this = self.sql(expression, "this")
    986        if expression.args.get("not_null"):
    987            persisted = " PERSISTED NOT NULL"
    988        elif expression.args.get("persisted"):
    989            persisted = " PERSISTED"
    990        else:
    991            persisted = ""
    992        return f"AS {this}{persisted}"

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

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

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

     997    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
     998        if isinstance(expression.this, list):
     999            this = self.wrap(self.expressions(expression, key="this", flat=True))
    1000        else:
    1001            this = self.sql(expression, "this")
    1002
    1003        return f"COMPRESS {this}"

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

    1005    def generatedasidentitycolumnconstraint_sql(
    1006        self, expression: exp.GeneratedAsIdentityColumnConstraint
    1007    ) -> str:
    1008        this = ""
    1009        if expression.this is not None:
    1010            on_null = " ON NULL" if expression.args.get("on_null") else ""
    1011            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    1012
    1013        start = expression.args.get("start")
    1014        start = f"START WITH {start}" if start else ""
    1015        increment = expression.args.get("increment")
    1016        increment = f" INCREMENT BY {increment}" if increment else ""
    1017        minvalue = expression.args.get("minvalue")
    1018        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1019        maxvalue = expression.args.get("maxvalue")
    1020        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1021        cycle = expression.args.get("cycle")
    1022        cycle_sql = ""
    1023
    1024        if cycle is not None:
    1025            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    1026            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    1027
    1028        sequence_opts = ""
    1029        if start or increment or cycle_sql:
    1030            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    1031            sequence_opts = f" ({sequence_opts.strip()})"
    1032
    1033        expr = self.sql(expression, "expression")
    1034        expr = f"({expr})" if expr else "IDENTITY"
    1035
    1036        return f"GENERATED{this} AS {expr}{sequence_opts}"

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

    1038    def generatedasrowcolumnconstraint_sql(
    1039        self, expression: exp.GeneratedAsRowColumnConstraint
    1040    ) -> str:
    1041        start = "START" if expression.args.get("start") else "END"
    1042        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    1043        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    1045    def periodforsystemtimeconstraint_sql(
    1046        self, expression: exp.PeriodForSystemTimeConstraint
    1047    ) -> str:
    1048        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"

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

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

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

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

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

    1056    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    1057        desc = expression.args.get("desc")
    1058        if desc is not None:
    1059            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    1060        return "PRIMARY KEY"

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

    1062    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    1063        this = self.sql(expression, "this")
    1064        this = f" {this}" if this else ""
    1065        index_type = expression.args.get("index_type")
    1066        index_type = f" USING {index_type}" if index_type else ""
    1067        on_conflict = self.sql(expression, "on_conflict")
    1068        on_conflict = f" {on_conflict}" if on_conflict else ""
    1069        nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else ""
    1070        return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}"

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

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

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

    1075    def create_sql(self, expression: exp.Create) -> str:
    1076        kind = self.sql(expression, "kind")
    1077        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1078        properties = expression.args.get("properties")
    1079        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    1080
    1081        this = self.createable_sql(expression, properties_locs)
    1082
    1083        properties_sql = ""
    1084        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    1085            exp.Properties.Location.POST_WITH
    1086        ):
    1087            properties_sql = self.sql(
    1088                exp.Properties(
    1089                    expressions=[
    1090                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    1091                        *properties_locs[exp.Properties.Location.POST_WITH],
    1092                    ]
    1093                )
    1094            )
    1095
    1096            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
    1097                properties_sql = self.sep() + properties_sql
    1098            elif not self.pretty:
    1099                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
    1100                properties_sql = f" {properties_sql}"
    1101
    1102        begin = " BEGIN" if expression.args.get("begin") else ""
    1103        end = " END" if expression.args.get("end") else ""
    1104
    1105        expression_sql = self.sql(expression, "expression")
    1106        if expression_sql:
    1107            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    1108
    1109            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    1110                postalias_props_sql = ""
    1111                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    1112                    postalias_props_sql = self.properties(
    1113                        exp.Properties(
    1114                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    1115                        ),
    1116                        wrapped=False,
    1117                    )
    1118                postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else ""
    1119                expression_sql = f" AS{postalias_props_sql}{expression_sql}"
    1120
    1121        postindex_props_sql = ""
    1122        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    1123            postindex_props_sql = self.properties(
    1124                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    1125                wrapped=False,
    1126                prefix=" ",
    1127            )
    1128
    1129        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1130        indexes = f" {indexes}" if indexes else ""
    1131        index_sql = indexes + postindex_props_sql
    1132
    1133        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1134        refresh = " OR REFRESH" if expression.args.get("refresh") else ""
    1135        unique = " UNIQUE" if expression.args.get("unique") else ""
    1136
    1137        clustered = expression.args.get("clustered")
    1138        if clustered is None:
    1139            clustered_sql = ""
    1140        elif clustered:
    1141            clustered_sql = " CLUSTERED COLUMNSTORE"
    1142        else:
    1143            clustered_sql = " NONCLUSTERED COLUMNSTORE"
    1144
    1145        postcreate_props_sql = ""
    1146        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1147            postcreate_props_sql = self.properties(
    1148                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1149                sep=" ",
    1150                prefix=" ",
    1151                wrapped=False,
    1152            )
    1153
    1154        modifiers = "".join((clustered_sql, replace, refresh, unique, postcreate_props_sql))
    1155
    1156        postexpression_props_sql = ""
    1157        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1158            postexpression_props_sql = self.properties(
    1159                exp.Properties(
    1160                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1161                ),
    1162                sep=" ",
    1163                prefix=" ",
    1164                wrapped=False,
    1165            )
    1166
    1167        concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1168        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1169        no_schema_binding = (
    1170            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1171        )
    1172
    1173        clone = self.sql(expression, "clone")
    1174        clone = f" {clone}" if clone else ""
    1175
    1176        if kind in self.EXPRESSION_PRECEDES_PROPERTIES_CREATABLES:
    1177            properties_expression = f"{expression_sql}{properties_sql}"
    1178        else:
    1179            properties_expression = f"{properties_sql}{expression_sql}"
    1180
    1181        expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_expression}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1182        return self.prepend_ctes(expression, expression_sql)

def sequenceproperties_sql(self, expression: sqlglot.expressions.SequenceProperties) -> str: View Source

    1184    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1185        start = self.sql(expression, "start")
    1186        start = f"START WITH {start}" if start else ""
    1187        increment = self.sql(expression, "increment")
    1188        increment = f" INCREMENT BY {increment}" if increment else ""
    1189        minvalue = self.sql(expression, "minvalue")
    1190        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1191        maxvalue = self.sql(expression, "maxvalue")
    1192        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1193        owned = self.sql(expression, "owned")
    1194        owned = f" OWNED BY {owned}" if owned else ""
    1195
    1196        cache = expression.args.get("cache")
    1197        if cache is None:
    1198            cache_str = ""
    1199        elif cache is True:
    1200            cache_str = " CACHE"
    1201        else:
    1202            cache_str = f" CACHE {cache}"
    1203
    1204        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1205        options = f" {options}" if options else ""
    1206
    1207        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()

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

    1209    def clone_sql(self, expression: exp.Clone) -> str:
    1210        this = self.sql(expression, "this")
    1211        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1212        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1213        return f"{shallow}{keyword} {this}"

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

    1215    def describe_sql(self, expression: exp.Describe) -> str:
    1216        style = expression.args.get("style")
    1217        style = f" {style}" if style else ""
    1218        partition = self.sql(expression, "partition")
    1219        partition = f" {partition}" if partition else ""
    1220        format = self.sql(expression, "format")
    1221        format = f" {format}" if format else ""
    1222
    1223        return f"DESCRIBE{style}{format} {self.sql(expression, 'this')}{partition}"

def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str: View Source

    1225    def heredoc_sql(self, expression: exp.Heredoc) -> str:
    1226        tag = self.sql(expression, "tag")
    1227        return f"${tag}${self.sql(expression, 'this')}${tag}$"

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

    1229    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1230        with_ = self.sql(expression, "with")
    1231        if with_:
    1232            sql = f"{with_}{self.sep()}{sql}"
    1233        return sql

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

    1235    def with_sql(self, expression: exp.With) -> str:
    1236        sql = self.expressions(expression, flat=True)
    1237        recursive = (
    1238            "RECURSIVE "
    1239            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1240            else ""
    1241        )
    1242
    1243        return f"WITH {recursive}{sql}"

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

    1245    def cte_sql(self, expression: exp.CTE) -> str:
    1246        alias = expression.args.get("alias")
    1247        if alias:
    1248            alias.add_comments(expression.pop_comments())
    1249
    1250        alias_sql = self.sql(expression, "alias")
    1251
    1252        materialized = expression.args.get("materialized")
    1253        if materialized is False:
    1254            materialized = "NOT MATERIALIZED "
    1255        elif materialized:
    1256            materialized = "MATERIALIZED "
    1257
    1258        return f"{alias_sql} AS {materialized or ''}{self.wrap(expression)}"

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

    1260    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1261        alias = self.sql(expression, "this")
    1262        columns = self.expressions(expression, key="columns", flat=True)
    1263        columns = f"({columns})" if columns else ""
    1264
    1265        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1266            columns = ""
    1267            self.unsupported("Named columns are not supported in table alias.")
    1268
    1269        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1270            alias = self._next_name()
    1271
    1272        return f"{alias}{columns}"

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

    1274    def bitstring_sql(self, expression: exp.BitString) -> str:
    1275        this = self.sql(expression, "this")
    1276        if self.dialect.BIT_START:
    1277            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1278        return f"{int(this, 2)}"

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

    1280    def hexstring_sql(self, expression: exp.HexString) -> str:
    1281        this = self.sql(expression, "this")
    1282        if self.dialect.HEX_START:
    1283            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1284        return f"{int(this, 16)}"

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

    1286    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1287        this = self.sql(expression, "this")
    1288        if self.dialect.BYTE_START:
    1289            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1290        return this

def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str: View Source

    1292    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1293        this = self.sql(expression, "this")
    1294        escape = expression.args.get("escape")
    1295
    1296        if self.dialect.UNICODE_START:
    1297            escape_substitute = r"\\\1"
    1298            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1299        else:
    1300            escape_substitute = r"\\u\1"
    1301            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1302
    1303        if escape:
    1304            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1305            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1306        else:
    1307            escape_pattern = ESCAPED_UNICODE_RE
    1308            escape_sql = ""
    1309
    1310        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1311            this = escape_pattern.sub(escape_substitute, this)
    1312
    1313        return f"{left_quote}{this}{right_quote}{escape_sql}"

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

    1315    def rawstring_sql(self, expression: exp.RawString) -> str:
    1316        string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False)
    1317        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"

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

    1319    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1320        this = self.sql(expression, "this")
    1321        specifier = self.sql(expression, "expression")
    1322        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1323        return f"{this}{specifier}"

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

    1325    def datatype_sql(self, expression: exp.DataType) -> str:
    1326        nested = ""
    1327        values = ""
    1328        interior = self.expressions(expression, flat=True)
    1329
    1330        type_value = expression.this
    1331        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1332            type_sql = self.sql(expression, "kind")
    1333        else:
    1334            type_sql = (
    1335                self.TYPE_MAPPING.get(type_value, type_value.value)
    1336                if isinstance(type_value, exp.DataType.Type)
    1337                else type_value
    1338            )
    1339
    1340        if interior:
    1341            if expression.args.get("nested"):
    1342                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1343                if expression.args.get("values") is not None:
    1344                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1345                    values = self.expressions(expression, key="values", flat=True)
    1346                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1347            elif type_value == exp.DataType.Type.INTERVAL:
    1348                nested = f" {interior}"
    1349            else:
    1350                nested = f"({interior})"
    1351
    1352        type_sql = f"{type_sql}{nested}{values}"
    1353        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1354            exp.DataType.Type.TIMETZ,
    1355            exp.DataType.Type.TIMESTAMPTZ,
    1356        ):
    1357            type_sql = f"{type_sql} WITH TIME ZONE"
    1358
    1359        return type_sql

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

    1361    def directory_sql(self, expression: exp.Directory) -> str:
    1362        local = "LOCAL " if expression.args.get("local") else ""
    1363        row_format = self.sql(expression, "row_format")
    1364        row_format = f" {row_format}" if row_format else ""
    1365        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1367    def delete_sql(self, expression: exp.Delete) -> str:
    1368        this = self.sql(expression, "this")
    1369        this = f" FROM {this}" if this else ""
    1370        using = self.sql(expression, "using")
    1371        using = f" USING {using}" if using else ""
    1372        cluster = self.sql(expression, "cluster")
    1373        cluster = f" {cluster}" if cluster else ""
    1374        where = self.sql(expression, "where")
    1375        returning = self.sql(expression, "returning")
    1376        limit = self.sql(expression, "limit")
    1377        tables = self.expressions(expression, key="tables")
    1378        tables = f" {tables}" if tables else ""
    1379        if self.RETURNING_END:
    1380            expression_sql = f"{this}{using}{cluster}{where}{returning}{limit}"
    1381        else:
    1382            expression_sql = f"{returning}{this}{using}{cluster}{where}{limit}"
    1383        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1385    def drop_sql(self, expression: exp.Drop) -> str:
    1386        this = self.sql(expression, "this")
    1387        expressions = self.expressions(expression, flat=True)
    1388        expressions = f" ({expressions})" if expressions else ""
    1389        kind = expression.args["kind"]
    1390        kind = self.dialect.INVERSE_CREATABLE_KIND_MAPPING.get(kind) or kind
    1391        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1392        concurrently_sql = " CONCURRENTLY" if expression.args.get("concurrently") else ""
    1393        on_cluster = self.sql(expression, "cluster")
    1394        on_cluster = f" {on_cluster}" if on_cluster else ""
    1395        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1396        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1397        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1398        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1399        purge = " PURGE" if expression.args.get("purge") else ""
    1400        return f"DROP{temporary}{materialized} {kind}{concurrently_sql}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"

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

    1402    def set_operation(self, expression: exp.SetOperation) -> str:
    1403        op_type = type(expression)
    1404        op_name = op_type.key.upper()
    1405
    1406        distinct = expression.args.get("distinct")
    1407        if (
    1408            distinct is False
    1409            and op_type in (exp.Except, exp.Intersect)
    1410            and not self.EXCEPT_INTERSECT_SUPPORT_ALL_CLAUSE
    1411        ):
    1412            self.unsupported(f"{op_name} ALL is not supported")
    1413
    1414        default_distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[op_type]
    1415
    1416        if distinct is None:
    1417            distinct = default_distinct
    1418            if distinct is None:
    1419                self.unsupported(f"{op_name} requires DISTINCT or ALL to be specified")
    1420
    1421        if distinct is default_distinct:
    1422            kind = ""
    1423        else:
    1424            kind = " DISTINCT" if distinct else " ALL"
    1425
    1426        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1427        return f"{op_name}{kind}{by_name}"

def set_operations(self, expression: sqlglot.expressions.SetOperation) -> str: View Source

    1429    def set_operations(self, expression: exp.SetOperation) -> str:
    1430        if not self.SET_OP_MODIFIERS:
    1431            limit = expression.args.get("limit")
    1432            order = expression.args.get("order")
    1433
    1434            if limit or order:
    1435                select = self._move_ctes_to_top_level(
    1436                    exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    1437                )
    1438
    1439                if limit:
    1440                    select = select.limit(limit.pop(), copy=False)
    1441                if order:
    1442                    select = select.order_by(order.pop(), copy=False)
    1443                return self.sql(select)
    1444
    1445        sqls: t.List[str] = []
    1446        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    1447
    1448        while stack:
    1449            node = stack.pop()
    1450
    1451            if isinstance(node, exp.SetOperation):
    1452                stack.append(node.expression)
    1453                stack.append(
    1454                    self.maybe_comment(
    1455                        self.set_operation(node), comments=node.comments, separated=True
    1456                    )
    1457                )
    1458                stack.append(node.this)
    1459            else:
    1460                sqls.append(self.sql(node))
    1461
    1462        this = self.sep().join(sqls)
    1463        this = self.query_modifiers(expression, this)
    1464        return self.prepend_ctes(expression, this)

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

    1466    def fetch_sql(self, expression: exp.Fetch) -> str:
    1467        direction = expression.args.get("direction")
    1468        direction = f" {direction}" if direction else ""
    1469        count = self.sql(expression, "count")
    1470        count = f" {count}" if count else ""
    1471        if expression.args.get("percent"):
    1472            count = f"{count} PERCENT"
    1473        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1474        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1476    def filter_sql(self, expression: exp.Filter) -> str:
    1477        if self.AGGREGATE_FILTER_SUPPORTED:
    1478            this = self.sql(expression, "this")
    1479            where = self.sql(expression, "expression").strip()
    1480            return f"{this} FILTER({where})"
    1481
    1482        agg = expression.this
    1483        agg_arg = agg.this
    1484        cond = expression.expression.this
    1485        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1486        return self.sql(agg)

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

    1488    def hint_sql(self, expression: exp.Hint) -> str:
    1489        if not self.QUERY_HINTS:
    1490            self.unsupported("Hints are not supported")
    1491            return ""
    1492
    1493        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

def indexparameters_sql(self, expression: sqlglot.expressions.IndexParameters) -> str: View Source

    1495    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1496        using = self.sql(expression, "using")
    1497        using = f" USING {using}" if using else ""
    1498        columns = self.expressions(expression, key="columns", flat=True)
    1499        columns = f"({columns})" if columns else ""
    1500        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1501        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1502        where = self.sql(expression, "where")
    1503        include = self.expressions(expression, key="include", flat=True)
    1504        if include:
    1505            include = f" INCLUDE ({include})"
    1506        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1507        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1508        tablespace = self.sql(expression, "tablespace")
    1509        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1510        on = self.sql(expression, "on")
    1511        on = f" ON {on}" if on else ""
    1512
    1513        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"

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

    1515    def index_sql(self, expression: exp.Index) -> str:
    1516        unique = "UNIQUE " if expression.args.get("unique") else ""
    1517        primary = "PRIMARY " if expression.args.get("primary") else ""
    1518        amp = "AMP " if expression.args.get("amp") else ""
    1519        name = self.sql(expression, "this")
    1520        name = f"{name} " if name else ""
    1521        table = self.sql(expression, "table")
    1522        table = f"{self.INDEX_ON} {table}" if table else ""
    1523
    1524        index = "INDEX " if not table else ""
    1525
    1526        params = self.sql(expression, "params")
    1527        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

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

    1529    def identifier_sql(self, expression: exp.Identifier) -> str:
    1530        text = expression.name
    1531        lower = text.lower()
    1532        text = lower if self.normalize and not expression.quoted else text
    1533        text = text.replace(self._identifier_end, self._escaped_identifier_end)
    1534        if (
    1535            expression.quoted
    1536            or self.dialect.can_identify(text, self.identify)
    1537            or lower in self.RESERVED_KEYWORDS
    1538            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1539        ):
    1540            text = f"{self._identifier_start}{text}{self._identifier_end}"
    1541        return text

def hex_sql(self, expression: sqlglot.expressions.Hex) -> str: View Source

    1543    def hex_sql(self, expression: exp.Hex) -> str:
    1544        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1545        if self.dialect.HEX_LOWERCASE:
    1546            text = self.func("LOWER", text)
    1547
    1548        return text

def lowerhex_sql(self, expression: sqlglot.expressions.LowerHex) -> str: View Source

    1550    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1551        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1552        if not self.dialect.HEX_LOWERCASE:
    1553            text = self.func("LOWER", text)
    1554        return text

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

    1556    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1557        input_format = self.sql(expression, "input_format")
    1558        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1559        output_format = self.sql(expression, "output_format")
    1560        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1561        return self.sep().join((input_format, output_format))

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

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

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

    1567    def partition_sql(self, expression: exp.Partition) -> str:
    1568        partition_keyword = "SUBPARTITION" if expression.args.get("subpartition") else "PARTITION"
    1569        return f"{partition_keyword}({self.expressions(expression, flat=True)})"

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

    1571    def properties_sql(self, expression: exp.Properties) -> str:
    1572        root_properties = []
    1573        with_properties = []
    1574
    1575        for p in expression.expressions:
    1576            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1577            if p_loc == exp.Properties.Location.POST_WITH:
    1578                with_properties.append(p)
    1579            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1580                root_properties.append(p)
    1581
    1582        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1583        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1584
    1585        if root_props and with_props and not self.pretty:
    1586            with_props = " " + with_props
    1587
    1588        return root_props + with_props

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

    1590    def root_properties(self, properties: exp.Properties) -> str:
    1591        if properties.expressions:
    1592            return self.expressions(properties, indent=False, sep=" ")
    1593        return ""

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

    1595    def properties(
    1596        self,
    1597        properties: exp.Properties,
    1598        prefix: str = "",
    1599        sep: str = ", ",
    1600        suffix: str = "",
    1601        wrapped: bool = True,
    1602    ) -> str:
    1603        if properties.expressions:
    1604            expressions = self.expressions(properties, sep=sep, indent=False)
    1605            if expressions:
    1606                expressions = self.wrap(expressions) if wrapped else expressions
    1607                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1608        return ""

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

    1610    def with_properties(self, properties: exp.Properties) -> str:
    1611        return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep=""))

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

    1613    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1614        properties_locs = defaultdict(list)
    1615        for p in properties.expressions:
    1616            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1617            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1618                properties_locs[p_loc].append(p)
    1619            else:
    1620                self.unsupported(f"Unsupported property {p.key}")
    1621
    1622        return properties_locs

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

    1624    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1625        if isinstance(expression.this, exp.Dot):
    1626            return self.sql(expression, "this")
    1627        return f"'{expression.name}'" if string_key else expression.name

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

    1629    def property_sql(self, expression: exp.Property) -> str:
    1630        property_cls = expression.__class__
    1631        if property_cls == exp.Property:
    1632            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1633
    1634        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1635        if not property_name:
    1636            self.unsupported(f"Unsupported property {expression.key}")
    1637
    1638        return f"{property_name}={self.sql(expression, 'this')}"

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

    1640    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1641        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1642            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1643            options = f" {options}" if options else ""
    1644
    1645            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1646            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1647                like = f"({like})"
    1648
    1649            return like
    1650
    1651        if expression.expressions:
    1652            self.unsupported("Transpilation of LIKE property options is unsupported")
    1653
    1654        select = exp.select("*").from_(expression.this).limit(0)
    1655        return f"AS {self.sql(select)}"

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

    1657    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1658        no = "NO " if expression.args.get("no") else ""
    1659        protection = " PROTECTION" if expression.args.get("protection") else ""
    1660        return f"{no}FALLBACK{protection}"

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

    1662    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1663        no = "NO " if expression.args.get("no") else ""
    1664        local = expression.args.get("local")
    1665        local = f"{local} " if local else ""
    1666        dual = "DUAL " if expression.args.get("dual") else ""
    1667        before = "BEFORE " if expression.args.get("before") else ""
    1668        after = "AFTER " if expression.args.get("after") else ""
    1669        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1671    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1672        freespace = self.sql(expression, "this")
    1673        percent = " PERCENT" if expression.args.get("percent") else ""
    1674        return f"FREESPACE={freespace}{percent}"

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

    1676    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1677        if expression.args.get("default"):
    1678            property = "DEFAULT"
    1679        elif expression.args.get("on"):
    1680            property = "ON"
    1681        else:
    1682            property = "OFF"
    1683        return f"CHECKSUM={property}"

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

    1685    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1686        if expression.args.get("no"):
    1687            return "NO MERGEBLOCKRATIO"
    1688        if expression.args.get("default"):
    1689            return "DEFAULT MERGEBLOCKRATIO"
    1690
    1691        percent = " PERCENT" if expression.args.get("percent") else ""
    1692        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1694    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1695        default = expression.args.get("default")
    1696        minimum = expression.args.get("minimum")
    1697        maximum = expression.args.get("maximum")
    1698        if default or minimum or maximum:
    1699            if default:
    1700                prop = "DEFAULT"
    1701            elif minimum:
    1702                prop = "MINIMUM"
    1703            else:
    1704                prop = "MAXIMUM"
    1705            return f"{prop} DATABLOCKSIZE"
    1706        units = expression.args.get("units")
    1707        units = f" {units}" if units else ""
    1708        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1710    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1711        autotemp = expression.args.get("autotemp")
    1712        always = expression.args.get("always")
    1713        default = expression.args.get("default")
    1714        manual = expression.args.get("manual")
    1715        never = expression.args.get("never")
    1716
    1717        if autotemp is not None:
    1718            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1719        elif always:
    1720            prop = "ALWAYS"
    1721        elif default:
    1722            prop = "DEFAULT"
    1723        elif manual:
    1724            prop = "MANUAL"
    1725        elif never:
    1726            prop = "NEVER"
    1727        return f"BLOCKCOMPRESSION={prop}"

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

    1729    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1730        no = expression.args.get("no")
    1731        no = " NO" if no else ""
    1732        concurrent = expression.args.get("concurrent")
    1733        concurrent = " CONCURRENT" if concurrent else ""
    1734        target = self.sql(expression, "target")
    1735        target = f" {target}" if target else ""
    1736        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

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

    1738    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1739        if isinstance(expression.this, list):
    1740            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1741        if expression.this:
    1742            modulus = self.sql(expression, "this")
    1743            remainder = self.sql(expression, "expression")
    1744            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1745
    1746        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1747        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1748        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1750    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1751        this = self.sql(expression, "this")
    1752
    1753        for_values_or_default = expression.expression
    1754        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1755            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1756        else:
    1757            for_values_or_default = " DEFAULT"
    1758
    1759        return f"PARTITION OF {this}{for_values_or_default}"

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

    1761    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1762        kind = expression.args.get("kind")
    1763        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1764        for_or_in = expression.args.get("for_or_in")
    1765        for_or_in = f" {for_or_in}" if for_or_in else ""
    1766        lock_type = expression.args.get("lock_type")
    1767        override = " OVERRIDE" if expression.args.get("override") else ""
    1768        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1770    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1771        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1772        statistics = expression.args.get("statistics")
    1773        statistics_sql = ""
    1774        if statistics is not None:
    1775            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1776        return f"{data_sql}{statistics_sql}"

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

    1778    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1779        this = self.sql(expression, "this")
    1780        this = f"HISTORY_TABLE={this}" if this else ""
    1781        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1782        data_consistency = (
    1783            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1784        )
    1785        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1786        retention_period = (
    1787            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1788        )
    1789
    1790        if this:
    1791            on_sql = self.func("ON", this, data_consistency, retention_period)
    1792        else:
    1793            on_sql = "ON" if expression.args.get("on") else "OFF"
    1794
    1795        sql = f"SYSTEM_VERSIONING={on_sql}"
    1796
    1797        return f"WITH({sql})" if expression.args.get("with") else sql

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

    1799    def insert_sql(self, expression: exp.Insert) -> str:
    1800        hint = self.sql(expression, "hint")
    1801        overwrite = expression.args.get("overwrite")
    1802
    1803        if isinstance(expression.this, exp.Directory):
    1804            this = " OVERWRITE" if overwrite else " INTO"
    1805        else:
    1806            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1807
    1808        stored = self.sql(expression, "stored")
    1809        stored = f" {stored}" if stored else ""
    1810        alternative = expression.args.get("alternative")
    1811        alternative = f" OR {alternative}" if alternative else ""
    1812        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1813        is_function = expression.args.get("is_function")
    1814        if is_function:
    1815            this = f"{this} FUNCTION"
    1816        this = f"{this} {self.sql(expression, 'this')}"
    1817
    1818        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1819        where = self.sql(expression, "where")
    1820        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1821        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1822        on_conflict = self.sql(expression, "conflict")
    1823        on_conflict = f" {on_conflict}" if on_conflict else ""
    1824        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1825        returning = self.sql(expression, "returning")
    1826
    1827        if self.RETURNING_END:
    1828            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1829        else:
    1830            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1831
    1832        partition_by = self.sql(expression, "partition")
    1833        partition_by = f" {partition_by}" if partition_by else ""
    1834        settings = self.sql(expression, "settings")
    1835        settings = f" {settings}" if settings else ""
    1836
    1837        source = self.sql(expression, "source")
    1838        source = f"TABLE {source}" if source else ""
    1839
    1840        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{partition_by}{settings}{where}{expression_sql}{source}"
    1841        return self.prepend_ctes(expression, sql)

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

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

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

    1846    def kill_sql(self, expression: exp.Kill) -> str:
    1847        kind = self.sql(expression, "kind")
    1848        kind = f" {kind}" if kind else ""
    1849        this = self.sql(expression, "this")
    1850        this = f" {this}" if this else ""
    1851        return f"KILL{kind}{this}"

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

    1853    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
    1854        return expression.name

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

    1856    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
    1857        return expression.name

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

    1859    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1860        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1861
    1862        constraint = self.sql(expression, "constraint")
    1863        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1864
    1865        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1866        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1867        action = self.sql(expression, "action")
    1868
    1869        expressions = self.expressions(expression, flat=True)
    1870        if expressions:
    1871            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1872            expressions = f" {set_keyword}{expressions}"
    1873
    1874        where = self.sql(expression, "where")
    1875        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}{where}"

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

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

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

    1880    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1881        fields = self.sql(expression, "fields")
    1882        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1883        escaped = self.sql(expression, "escaped")
    1884        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1885        items = self.sql(expression, "collection_items")
    1886        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1887        keys = self.sql(expression, "map_keys")
    1888        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1889        lines = self.sql(expression, "lines")
    1890        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1891        null = self.sql(expression, "null")
    1892        null = f" NULL DEFINED AS {null}" if null else ""
    1893        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1898    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1899        this = f"{self.sql(expression, 'this')} INDEX"
    1900        target = self.sql(expression, "target")
    1901        target = f" FOR {target}" if target else ""
    1902        return f"{this}{target} ({self.expressions(expression, flat=True)})"

def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str: View Source

    1904    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1905        this = self.sql(expression, "this")
    1906        kind = self.sql(expression, "kind")
    1907        expr = self.sql(expression, "expression")
    1908        return f"{this} ({kind} => {expr})"

def table_parts(self, expression: sqlglot.expressions.Table) -> str: View Source

    1910    def table_parts(self, expression: exp.Table) -> str:
    1911        return ".".join(
    1912            self.sql(part)
    1913            for part in (
    1914                expression.args.get("catalog"),
    1915                expression.args.get("db"),
    1916                expression.args.get("this"),
    1917            )
    1918            if part is not None
    1919        )

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

    1921    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1922        table = self.table_parts(expression)
    1923        only = "ONLY " if expression.args.get("only") else ""
    1924        partition = self.sql(expression, "partition")
    1925        partition = f" {partition}" if partition else ""
    1926        version = self.sql(expression, "version")
    1927        version = f" {version}" if version else ""
    1928        alias = self.sql(expression, "alias")
    1929        alias = f"{sep}{alias}" if alias else ""
    1930
    1931        sample = self.sql(expression, "sample")
    1932        if self.dialect.ALIAS_POST_TABLESAMPLE:
    1933            sample_pre_alias = sample
    1934            sample_post_alias = ""
    1935        else:
    1936            sample_pre_alias = ""
    1937            sample_post_alias = sample
    1938
    1939        hints = self.expressions(expression, key="hints", sep=" ")
    1940        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1941        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    1942        joins = self.indent(
    1943            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1944        )
    1945        laterals = self.expressions(expression, key="laterals", sep="")
    1946
    1947        file_format = self.sql(expression, "format")
    1948        if file_format:
    1949            pattern = self.sql(expression, "pattern")
    1950            pattern = f", PATTERN => {pattern}" if pattern else ""
    1951            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1952
    1953        ordinality = expression.args.get("ordinality") or ""
    1954        if ordinality:
    1955            ordinality = f" WITH ORDINALITY{alias}"
    1956            alias = ""
    1957
    1958        when = self.sql(expression, "when")
    1959        if when:
    1960            table = f"{table} {when}"
    1961
    1962        changes = self.sql(expression, "changes")
    1963        changes = f" {changes}" if changes else ""
    1964
    1965        rows_from = self.expressions(expression, key="rows_from")
    1966        if rows_from:
    1967            table = f"ROWS FROM {self.wrap(rows_from)}"
    1968
    1969        return f"{only}{table}{changes}{partition}{version}{file_format}{sample_pre_alias}{alias}{hints}{pivots}{sample_post_alias}{joins}{laterals}{ordinality}"

def tablesample_sql( self, expression: sqlglot.expressions.TableSample, tablesample_keyword: Optional[str] = None) -> str: View Source

    1971    def tablesample_sql(
    1972        self,
    1973        expression: exp.TableSample,
    1974        tablesample_keyword: t.Optional[str] = None,
    1975    ) -> str:
    1976        method = self.sql(expression, "method")
    1977        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1978        numerator = self.sql(expression, "bucket_numerator")
    1979        denominator = self.sql(expression, "bucket_denominator")
    1980        field = self.sql(expression, "bucket_field")
    1981        field = f" ON {field}" if field else ""
    1982        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1983        seed = self.sql(expression, "seed")
    1984        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1985
    1986        size = self.sql(expression, "size")
    1987        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1988            size = f"{size} ROWS"
    1989
    1990        percent = self.sql(expression, "percent")
    1991        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1992            percent = f"{percent} PERCENT"
    1993
    1994        expr = f"{bucket}{percent}{size}"
    1995        if self.TABLESAMPLE_REQUIRES_PARENS:
    1996            expr = f"({expr})"
    1997
    1998        return f" {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}"

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

    2000    def pivot_sql(self, expression: exp.Pivot) -> str:
    2001        expressions = self.expressions(expression, flat=True)
    2002        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    2003
    2004        if expression.this:
    2005            this = self.sql(expression, "this")
    2006            if not expressions:
    2007                return f"UNPIVOT {this}"
    2008
    2009            on = f"{self.seg('ON')} {expressions}"
    2010            into = self.sql(expression, "into")
    2011            into = f"{self.seg('INTO')} {into}" if into else ""
    2012            using = self.expressions(expression, key="using", flat=True)
    2013            using = f"{self.seg('USING')} {using}" if using else ""
    2014            group = self.sql(expression, "group")
    2015            return f"{direction} {this}{on}{into}{using}{group}"
    2016
    2017        alias = self.sql(expression, "alias")
    2018        alias = f" AS {alias}" if alias else ""
    2019
    2020        field = self.sql(expression, "field")
    2021
    2022        include_nulls = expression.args.get("include_nulls")
    2023        if include_nulls is not None:
    2024            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    2025        else:
    2026            nulls = ""
    2027
    2028        default_on_null = self.sql(expression, "default_on_null")
    2029        default_on_null = f" DEFAULT ON NULL ({default_on_null})" if default_on_null else ""
    2030        return f"{self.seg(direction)}{nulls}({expressions} FOR {field}{default_on_null}){alias}"

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

    2032    def version_sql(self, expression: exp.Version) -> str:
    2033        this = f"FOR {expression.name}"
    2034        kind = expression.text("kind")
    2035        expr = self.sql(expression, "expression")
    2036        return f"{this} {kind} {expr}"

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

    2038    def tuple_sql(self, expression: exp.Tuple) -> str:
    2039        return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})"

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

    2041    def update_sql(self, expression: exp.Update) -> str:
    2042        this = self.sql(expression, "this")
    2043        set_sql = self.expressions(expression, flat=True)
    2044        from_sql = self.sql(expression, "from")
    2045        where_sql = self.sql(expression, "where")
    2046        returning = self.sql(expression, "returning")
    2047        order = self.sql(expression, "order")
    2048        limit = self.sql(expression, "limit")
    2049        if self.RETURNING_END:
    2050            expression_sql = f"{from_sql}{where_sql}{returning}"
    2051        else:
    2052            expression_sql = f"{returning}{from_sql}{where_sql}"
    2053        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    2054        return self.prepend_ctes(expression, sql)

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

    2056    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    2057        values_as_table = values_as_table and self.VALUES_AS_TABLE
    2058
    2059        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    2060        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    2061            args = self.expressions(expression)
    2062            alias = self.sql(expression, "alias")
    2063            values = f"VALUES{self.seg('')}{args}"
    2064            values = (
    2065                f"({values})"
    2066                if self.WRAP_DERIVED_VALUES
    2067                and (alias or isinstance(expression.parent, (exp.From, exp.Table)))
    2068                else values
    2069            )
    2070            return f"{values} AS {alias}" if alias else values
    2071
    2072        # Converts `VALUES...` expression into a series of select unions.
    2073        alias_node = expression.args.get("alias")
    2074        column_names = alias_node and alias_node.columns
    2075
    2076        selects: t.List[exp.Query] = []
    2077
    2078        for i, tup in enumerate(expression.expressions):
    2079            row = tup.expressions
    2080
    2081            if i == 0 and column_names:
    2082                row = [
    2083                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    2084                ]
    2085
    2086            selects.append(exp.Select(expressions=row))
    2087
    2088        if self.pretty:
    2089            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    2090            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    2091            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    2092            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    2093            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    2094
    2095        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    2096        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    2097        return f"({unions}){alias}"

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

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

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

    2102    @unsupported_args("expressions")
    2103    def into_sql(self, expression: exp.Into) -> str:
    2104        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    2105        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
    2106        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"

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

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

def groupingsets_sql(self, expression: sqlglot.expressions.GroupingSets) -> str: View Source

    2111    def groupingsets_sql(self, expression: exp.GroupingSets) -> str:
    2112        grouping_sets = self.expressions(expression, indent=False)
    2113        return f"GROUPING SETS {self.wrap(grouping_sets)}"

def rollup_sql(self, expression: sqlglot.expressions.Rollup) -> str: View Source

    2115    def rollup_sql(self, expression: exp.Rollup) -> str:
    2116        expressions = self.expressions(expression, indent=False)
    2117        return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"

def cube_sql(self, expression: sqlglot.expressions.Cube) -> str: View Source

    2119    def cube_sql(self, expression: exp.Cube) -> str:
    2120        expressions = self.expressions(expression, indent=False)
    2121        return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"

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

    2123    def group_sql(self, expression: exp.Group) -> str:
    2124        group_by_all = expression.args.get("all")
    2125        if group_by_all is True:
    2126            modifier = " ALL"
    2127        elif group_by_all is False:
    2128            modifier = " DISTINCT"
    2129        else:
    2130            modifier = ""
    2131
    2132        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    2133
    2134        grouping_sets = self.expressions(expression, key="grouping_sets")
    2135        cube = self.expressions(expression, key="cube")
    2136        rollup = self.expressions(expression, key="rollup")
    2137
    2138        groupings = csv(
    2139            self.seg(grouping_sets) if grouping_sets else "",
    2140            self.seg(cube) if cube else "",
    2141            self.seg(rollup) if rollup else "",
    2142            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    2143            sep=self.GROUPINGS_SEP,
    2144        )
    2145
    2146        if (
    2147            expression.expressions
    2148            and groupings
    2149            and groupings.strip() not in ("WITH CUBE", "WITH ROLLUP")
    2150        ):
    2151            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    2152
    2153        return f"{group_by}{groupings}"

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

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

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

    2159    def connect_sql(self, expression: exp.Connect) -> str:
    2160        start = self.sql(expression, "start")
    2161        start = self.seg(f"START WITH {start}") if start else ""
    2162        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    2163        connect = self.sql(expression, "connect")
    2164        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    2165        return start + connect

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

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

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

    2170    def join_sql(self, expression: exp.Join) -> str:
    2171        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    2172            side = None
    2173        else:
    2174            side = expression.side
    2175
    2176        op_sql = " ".join(
    2177            op
    2178            for op in (
    2179                expression.method,
    2180                "GLOBAL" if expression.args.get("global") else None,
    2181                side,
    2182                expression.kind,
    2183                expression.hint if self.JOIN_HINTS else None,
    2184            )
    2185            if op
    2186        )
    2187        match_cond = self.sql(expression, "match_condition")
    2188        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    2189        on_sql = self.sql(expression, "on")
    2190        using = expression.args.get("using")
    2191
    2192        if not on_sql and using:
    2193            on_sql = csv(*(self.sql(column) for column in using))
    2194
    2195        this = expression.this
    2196        this_sql = self.sql(this)
    2197
    2198        exprs = self.expressions(expression)
    2199        if exprs:
    2200            this_sql = f"{this_sql},{self.seg(exprs)}"
    2201
    2202        if on_sql:
    2203            on_sql = self.indent(on_sql, skip_first=True)
    2204            space = self.seg(" " * self.pad) if self.pretty else " "
    2205            if using:
    2206                on_sql = f"{space}USING ({on_sql})"
    2207            else:
    2208                on_sql = f"{space}ON {on_sql}"
    2209        elif not op_sql:
    2210            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    2211                return f" {this_sql}"
    2212
    2213            return f", {this_sql}"
    2214
    2215        if op_sql != "STRAIGHT_JOIN":
    2216            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    2217
    2218        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"

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

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

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

    2225    def lateral_op(self, expression: exp.Lateral) -> str:
    2226        cross_apply = expression.args.get("cross_apply")
    2227
    2228        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2229        if cross_apply is True:
    2230            op = "INNER JOIN "
    2231        elif cross_apply is False:
    2232            op = "LEFT JOIN "
    2233        else:
    2234            op = ""
    2235
    2236        return f"{op}LATERAL"

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

    2238    def lateral_sql(self, expression: exp.Lateral) -> str:
    2239        this = self.sql(expression, "this")
    2240
    2241        if expression.args.get("view"):
    2242            alias = expression.args["alias"]
    2243            columns = self.expressions(alias, key="columns", flat=True)
    2244            table = f" {alias.name}" if alias.name else ""
    2245            columns = f" AS {columns}" if columns else ""
    2246            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2247            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2248
    2249        alias = self.sql(expression, "alias")
    2250        alias = f" AS {alias}" if alias else ""
    2251        return f"{self.lateral_op(expression)} {this}{alias}"

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

    2253    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2254        this = self.sql(expression, "this")
    2255
    2256        args = [
    2257            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2258            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2259            if e
    2260        ]
    2261
    2262        args_sql = ", ".join(self.sql(e) for e in args)
    2263        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2264        expressions = self.expressions(expression, flat=True)
    2265        expressions = f" BY {expressions}" if expressions else ""
    2266
    2267        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"

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

    2269    def offset_sql(self, expression: exp.Offset) -> str:
    2270        this = self.sql(expression, "this")
    2271        value = expression.expression
    2272        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2273        expressions = self.expressions(expression, flat=True)
    2274        expressions = f" BY {expressions}" if expressions else ""
    2275        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    2277    def setitem_sql(self, expression: exp.SetItem) -> str:
    2278        kind = self.sql(expression, "kind")
    2279        kind = f"{kind} " if kind else ""
    2280        this = self.sql(expression, "this")
    2281        expressions = self.expressions(expression)
    2282        collate = self.sql(expression, "collate")
    2283        collate = f" COLLATE {collate}" if collate else ""
    2284        global_ = "GLOBAL " if expression.args.get("global") else ""
    2285        return f"{global_}{kind}{this}{expressions}{collate}"

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

    2287    def set_sql(self, expression: exp.Set) -> str:
    2288        expressions = (
    2289            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    2290        )
    2291        tag = " TAG" if expression.args.get("tag") else ""
    2292        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    2297    def lock_sql(self, expression: exp.Lock) -> str:
    2298        if not self.LOCKING_READS_SUPPORTED:
    2299            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2300            return ""
    2301
    2302        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2303        expressions = self.expressions(expression, flat=True)
    2304        expressions = f" OF {expressions}" if expressions else ""
    2305        wait = expression.args.get("wait")
    2306
    2307        if wait is not None:
    2308            if isinstance(wait, exp.Literal):
    2309                wait = f" WAIT {self.sql(wait)}"
    2310            else:
    2311                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2312
    2313        return f"{lock_type}{expressions}{wait or ''}"

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

    2315    def literal_sql(self, expression: exp.Literal) -> str:
    2316        text = expression.this or ""
    2317        if expression.is_string:
    2318            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2319        return text

def escape_str(self, text: str, escape_backslash: bool = True) -> str: View Source

    2321    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2322        if self.dialect.ESCAPED_SEQUENCES:
    2323            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2324            text = "".join(
    2325                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2326            )
    2327
    2328        return self._replace_line_breaks(text).replace(
    2329            self.dialect.QUOTE_END, self._escaped_quote_end
    2330        )

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

    2332    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2333        local = " LOCAL" if expression.args.get("local") else ""
    2334        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2335        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2336        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2337        partition = self.sql(expression, "partition")
    2338        partition = f" {partition}" if partition else ""
    2339        input_format = self.sql(expression, "input_format")
    2340        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2341        serde = self.sql(expression, "serde")
    2342        serde = f" SERDE {serde}" if serde else ""
    2343        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    2345    def null_sql(self, *_) -> str:
    2346        return "NULL"

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

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

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

    2351    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2352        this = self.sql(expression, "this")
    2353        this = f"{this} " if this else this
    2354        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2355        return self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore

def withfill_sql(self, expression: sqlglot.expressions.WithFill) -> str: View Source

    2357    def withfill_sql(self, expression: exp.WithFill) -> str:
    2358        from_sql = self.sql(expression, "from")
    2359        from_sql = f" FROM {from_sql}" if from_sql else ""
    2360        to_sql = self.sql(expression, "to")
    2361        to_sql = f" TO {to_sql}" if to_sql else ""
    2362        step_sql = self.sql(expression, "step")
    2363        step_sql = f" STEP {step_sql}" if step_sql else ""
    2364        interpolated_values = [
    2365            f"{self.sql(e, 'alias')} AS {self.sql(e, 'this')}"
    2366            if isinstance(e, exp.Alias)
    2367            else self.sql(e, "this")
    2368            for e in expression.args.get("interpolate") or []
    2369        ]
    2370        interpolate = (
    2371            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2372        )
    2373        return f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"

def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str: View Source

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

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

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

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

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

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

    2384    def ordered_sql(self, expression: exp.Ordered) -> str:
    2385        desc = expression.args.get("desc")
    2386        asc = not desc
    2387
    2388        nulls_first = expression.args.get("nulls_first")
    2389        nulls_last = not nulls_first
    2390        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2391        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2392        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2393
    2394        this = self.sql(expression, "this")
    2395
    2396        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2397        nulls_sort_change = ""
    2398        if nulls_first and (
    2399            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2400        ):
    2401            nulls_sort_change = " NULLS FIRST"
    2402        elif (
    2403            nulls_last
    2404            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2405            and not nulls_are_last
    2406        ):
    2407            nulls_sort_change = " NULLS LAST"
    2408
    2409        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2410        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2411            window = expression.find_ancestor(exp.Window, exp.Select)
    2412            if isinstance(window, exp.Window) and window.args.get("spec"):
    2413                self.unsupported(
    2414                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2415                )
    2416                nulls_sort_change = ""
    2417            elif self.NULL_ORDERING_SUPPORTED is False and (
    2418                (asc and nulls_sort_change == " NULLS LAST")
    2419                or (desc and nulls_sort_change == " NULLS FIRST")
    2420            ):
    2421                # BigQuery does not allow these ordering/nulls combinations when used under
    2422                # an aggregation func or under a window containing one
    2423                ancestor = expression.find_ancestor(exp.AggFunc, exp.Window, exp.Select)
    2424
    2425                if isinstance(ancestor, exp.Window):
    2426                    ancestor = ancestor.this
    2427                if isinstance(ancestor, exp.AggFunc):
    2428                    self.unsupported(
    2429                        f"'{nulls_sort_change.strip()}' translation not supported for aggregate functions with {sort_order} sort order"
    2430                    )
    2431                    nulls_sort_change = ""
    2432            elif self.NULL_ORDERING_SUPPORTED is None:
    2433                if expression.this.is_int:
    2434                    self.unsupported(
    2435                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2436                    )
    2437                elif not isinstance(expression.this, exp.Rand):
    2438                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2439                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2440                nulls_sort_change = ""
    2441
    2442        with_fill = self.sql(expression, "with_fill")
    2443        with_fill = f" {with_fill}" if with_fill else ""
    2444
    2445        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

def matchrecognizemeasure_sql(self, expression: sqlglot.expressions.MatchRecognizeMeasure) -> str: View Source

    2447    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2448        window_frame = self.sql(expression, "window_frame")
    2449        window_frame = f"{window_frame} " if window_frame else ""
    2450
    2451        this = self.sql(expression, "this")
    2452
    2453        return f"{window_frame}{this}"

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

    2455    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2456        partition = self.partition_by_sql(expression)
    2457        order = self.sql(expression, "order")
    2458        measures = self.expressions(expression, key="measures")
    2459        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2460        rows = self.sql(expression, "rows")
    2461        rows = self.seg(rows) if rows else ""
    2462        after = self.sql(expression, "after")
    2463        after = self.seg(after) if after else ""
    2464        pattern = self.sql(expression, "pattern")
    2465        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2466        definition_sqls = [
    2467            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2468            for definition in expression.args.get("define", [])
    2469        ]
    2470        definitions = self.expressions(sqls=definition_sqls)
    2471        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2472        body = "".join(
    2473            (
    2474                partition,
    2475                order,
    2476                measures,
    2477                rows,
    2478                after,
    2479                pattern,
    2480                define,
    2481            )
    2482        )
    2483        alias = self.sql(expression, "alias")
    2484        alias = f" {alias}" if alias else ""
    2485        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2487    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2488        limit = expression.args.get("limit")
    2489
    2490        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2491            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2492        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2493            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2494
    2495        return csv(
    2496            *sqls,
    2497            *[self.sql(join) for join in expression.args.get("joins") or []],
    2498            self.sql(expression, "match"),
    2499            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2500            self.sql(expression, "prewhere"),
    2501            self.sql(expression, "where"),
    2502            self.sql(expression, "connect"),
    2503            self.sql(expression, "group"),
    2504            self.sql(expression, "having"),
    2505            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2506            self.sql(expression, "order"),
    2507            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2508            *self.after_limit_modifiers(expression),
    2509            self.options_modifier(expression),
    2510            sep="",
    2511        )

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

    2513    def options_modifier(self, expression: exp.Expression) -> str:
    2514        options = self.expressions(expression, key="options")
    2515        return f" {options}" if options else ""

def queryoption_sql(self, expression: sqlglot.expressions.QueryOption) -> str: View Source

    2517    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2518        return ""

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

    2520    def offset_limit_modifiers(
    2521        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2522    ) -> t.List[str]:
    2523        return [
    2524            self.sql(expression, "offset") if fetch else self.sql(limit),
    2525            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2526        ]

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

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

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

    2533    def select_sql(self, expression: exp.Select) -> str:
    2534        into = expression.args.get("into")
    2535        if not self.SUPPORTS_SELECT_INTO and into:
    2536            into.pop()
    2537
    2538        hint = self.sql(expression, "hint")
    2539        distinct = self.sql(expression, "distinct")
    2540        distinct = f" {distinct}" if distinct else ""
    2541        kind = self.sql(expression, "kind")
    2542
    2543        limit = expression.args.get("limit")
    2544        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2545            top = self.limit_sql(limit, top=True)
    2546            limit.pop()
    2547        else:
    2548            top = ""
    2549
    2550        expressions = self.expressions(expression)
    2551
    2552        if kind:
    2553            if kind in self.SELECT_KINDS:
    2554                kind = f" AS {kind}"
    2555            else:
    2556                if kind == "STRUCT":
    2557                    expressions = self.expressions(
    2558                        sqls=[
    2559                            self.sql(
    2560                                exp.Struct(
    2561                                    expressions=[
    2562                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2563                                        if isinstance(e, exp.Alias)
    2564                                        else e
    2565                                        for e in expression.expressions
    2566                                    ]
    2567                                )
    2568                            )
    2569                        ]
    2570                    )
    2571                kind = ""
    2572
    2573        operation_modifiers = self.expressions(expression, key="operation_modifiers", sep=" ")
    2574        operation_modifiers = f"{self.sep()}{operation_modifiers}" if operation_modifiers else ""
    2575
    2576        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2577        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2578        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2579        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2580        sql = self.query_modifiers(
    2581            expression,
    2582            f"SELECT{top_distinct}{operation_modifiers}{kind}{expressions}",
    2583            self.sql(expression, "into", comment=False),
    2584            self.sql(expression, "from", comment=False),
    2585        )
    2586
    2587        # If both the CTE and SELECT clauses have comments, generate the latter earlier
    2588        if expression.args.get("with"):
    2589            sql = self.maybe_comment(sql, expression)
    2590            expression.pop_comments()
    2591
    2592        sql = self.prepend_ctes(expression, sql)
    2593
    2594        if not self.SUPPORTS_SELECT_INTO and into:
    2595            if into.args.get("temporary"):
    2596                table_kind = " TEMPORARY"
    2597            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2598                table_kind = " UNLOGGED"
    2599            else:
    2600                table_kind = ""
    2601            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2602
    2603        return sql

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

    2605    def schema_sql(self, expression: exp.Schema) -> str:
    2606        this = self.sql(expression, "this")
    2607        sql = self.schema_columns_sql(expression)
    2608        return f"{this} {sql}" if this and sql else this or sql

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

    2610    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2611        if expression.expressions:
    2612            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2613        return ""

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

    2615    def star_sql(self, expression: exp.Star) -> str:
    2616        except_ = self.expressions(expression, key="except", flat=True)
    2617        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2618        replace = self.expressions(expression, key="replace", flat=True)
    2619        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2620        rename = self.expressions(expression, key="rename", flat=True)
    2621        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2622        return f"*{except_}{replace}{rename}"

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

    2624    def parameter_sql(self, expression: exp.Parameter) -> str:
    2625        this = self.sql(expression, "this")
    2626        return f"{self.PARAMETER_TOKEN}{this}"

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

    2628    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2629        this = self.sql(expression, "this")
    2630        kind = expression.text("kind")
    2631        if kind:
    2632            kind = f"{kind}."
    2633        return f"@@{kind}{this}"

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

    2635    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2636        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?"

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

    2638    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2639        alias = self.sql(expression, "alias")
    2640        alias = f"{sep}{alias}" if alias else ""
    2641        sample = self.sql(expression, "sample")
    2642        if self.dialect.ALIAS_POST_TABLESAMPLE and sample:
    2643            alias = f"{sample}{alias}"
    2644
    2645            # Set to None so it's not generated again by self.query_modifiers()
    2646            expression.set("sample", None)
    2647
    2648        pivots = self.expressions(expression, key="pivots", sep="", flat=True)
    2649        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2650        return self.prepend_ctes(expression, sql)

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

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

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

    2656    def unnest_sql(self, expression: exp.Unnest) -> str:
    2657        args = self.expressions(expression, flat=True)
    2658
    2659        alias = expression.args.get("alias")
    2660        offset = expression.args.get("offset")
    2661
    2662        if self.UNNEST_WITH_ORDINALITY:
    2663            if alias and isinstance(offset, exp.Expression):
    2664                alias.append("columns", offset)
    2665
    2666        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2667            columns = alias.columns
    2668            alias = self.sql(columns[0]) if columns else ""
    2669        else:
    2670            alias = self.sql(alias)
    2671
    2672        alias = f" AS {alias}" if alias else alias
    2673        if self.UNNEST_WITH_ORDINALITY:
    2674            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2675        else:
    2676            if isinstance(offset, exp.Expression):
    2677                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2678            elif offset:
    2679                suffix = f"{alias} WITH OFFSET"
    2680            else:
    2681                suffix = alias
    2682
    2683        return f"UNNEST({args}){suffix}"

def prewhere_sql(self, expression: sqlglot.expressions.PreWhere) -> str: View Source

    2685    def prewhere_sql(self, expression: exp.PreWhere) -> str:
    2686        return ""

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

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

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

    2692    def window_sql(self, expression: exp.Window) -> str:
    2693        this = self.sql(expression, "this")
    2694        partition = self.partition_by_sql(expression)
    2695        order = expression.args.get("order")
    2696        order = self.order_sql(order, flat=True) if order else ""
    2697        spec = self.sql(expression, "spec")
    2698        alias = self.sql(expression, "alias")
    2699        over = self.sql(expression, "over") or "OVER"
    2700
    2701        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2702
    2703        first = expression.args.get("first")
    2704        if first is None:
    2705            first = ""
    2706        else:
    2707            first = "FIRST" if first else "LAST"
    2708
    2709        if not partition and not order and not spec and alias:
    2710            return f"{this} {alias}"
    2711
    2712        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2713        return f"{this} ({args})"

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

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

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

    2719    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2720        kind = self.sql(expression, "kind")
    2721        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2722        end = (
    2723            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2724            or "CURRENT ROW"
    2725        )
    2726        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2733    def between_sql(self, expression: exp.Between) -> str:
    2734        this = self.sql(expression, "this")
    2735        low = self.sql(expression, "low")
    2736        high = self.sql(expression, "high")
    2737        return f"{this} BETWEEN {low} AND {high}"

def bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket, index_offset: Optional[int] = None) -> List[sqlglot.expressions.Expression]: View Source

    2739    def bracket_offset_expressions(
    2740        self, expression: exp.Bracket, index_offset: t.Optional[int] = None
    2741    ) -> t.List[exp.Expression]:
    2742        return apply_index_offset(
    2743            expression.this,
    2744            expression.expressions,
    2745            (index_offset or self.dialect.INDEX_OFFSET) - expression.args.get("offset", 0),
    2746        )

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

    2748    def bracket_sql(self, expression: exp.Bracket) -> str:
    2749        expressions = self.bracket_offset_expressions(expression)
    2750        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2751        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    2756    def any_sql(self, expression: exp.Any) -> str:
    2757        this = self.sql(expression, "this")
    2758        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2759            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2760                this = self.wrap(this)
    2761            return f"ANY{this}"
    2762        return f"ANY {this}"

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

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

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

    2767    def case_sql(self, expression: exp.Case) -> str:
    2768        this = self.sql(expression, "this")
    2769        statements = [f"CASE {this}" if this else "CASE"]
    2770
    2771        for e in expression.args["ifs"]:
    2772            statements.append(f"WHEN {self.sql(e, 'this')}")
    2773            statements.append(f"THEN {self.sql(e, 'true')}")
    2774
    2775        default = self.sql(expression, "default")
    2776
    2777        if default:
    2778            statements.append(f"ELSE {default}")
    2779
    2780        statements.append("END")
    2781
    2782        if self.pretty and self.too_wide(statements):
    2783            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2784
    2785        return " ".join(statements)

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

    2787    def constraint_sql(self, expression: exp.Constraint) -> str:
    2788        this = self.sql(expression, "this")
    2789        expressions = self.expressions(expression, flat=True)
    2790        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2797    def extract_sql(self, expression: exp.Extract) -> str:
    2798        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2799        expression_sql = self.sql(expression, "expression")
    2800        return f"EXTRACT({this} FROM {expression_sql})"

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

    2802    def trim_sql(self, expression: exp.Trim) -> str:
    2803        trim_type = self.sql(expression, "position")
    2804
    2805        if trim_type == "LEADING":
    2806            func_name = "LTRIM"
    2807        elif trim_type == "TRAILING":
    2808            func_name = "RTRIM"
    2809        else:
    2810            func_name = "TRIM"
    2811
    2812        return self.func(func_name, expression.this, expression.expression)

def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]: View Source

    2814    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2815        args = expression.expressions
    2816        if isinstance(expression, exp.ConcatWs):
    2817            args = args[1:]  # Skip the delimiter
    2818
    2819        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2820            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2821
    2822        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2823            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2824
    2825        return args

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

    2827    def concat_sql(self, expression: exp.Concat) -> str:
    2828        expressions = self.convert_concat_args(expression)
    2829
    2830        # Some dialects don't allow a single-argument CONCAT call
    2831        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2832            return self.sql(expressions[0])
    2833
    2834        return self.func("CONCAT", *expressions)

def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str: View Source

    2836    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2837        return self.func(
    2838            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2839        )

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

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

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

    2845    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2846        expressions = self.expressions(expression, flat=True)
    2847        expressions = f" ({expressions})" if expressions else ""
    2848        reference = self.sql(expression, "reference")
    2849        reference = f" {reference}" if reference else ""
    2850        delete = self.sql(expression, "delete")
    2851        delete = f" ON DELETE {delete}" if delete else ""
    2852        update = self.sql(expression, "update")
    2853        update = f" ON UPDATE {update}" if update else ""
    2854        return f"FOREIGN KEY{expressions}{reference}{delete}{update}"

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

    2856    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2857        expressions = self.expressions(expression, flat=True)
    2858        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2859        options = f" {options}" if options else ""
    2860        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

def jsonpath_sql(self, expression: sqlglot.expressions.JSONPath) -> str: View Source

    2873    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2874        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2875
    2876        if expression.args.get("escape"):
    2877            path = self.escape_str(path)
    2878
    2879        if self.QUOTE_JSON_PATH:
    2880            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2881
    2882        return path

def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str: View Source

    2884    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2885        if isinstance(expression, exp.JSONPathPart):
    2886            transform = self.TRANSFORMS.get(expression.__class__)
    2887            if not callable(transform):
    2888                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2889                return ""
    2890
    2891            return transform(self, expression)
    2892
    2893        if isinstance(expression, int):
    2894            return str(expression)
    2895
    2896        if self._quote_json_path_key_using_brackets and self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2897            escaped = expression.replace("'", "\\'")
    2898            escaped = f"\\'{expression}\\'"
    2899        else:
    2900            escaped = expression.replace('"', '\\"')
    2901            escaped = f'"{escaped}"'
    2902
    2903        return escaped

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

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

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

    2908    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2909        null_handling = expression.args.get("null_handling")
    2910        null_handling = f" {null_handling}" if null_handling else ""
    2911
    2912        unique_keys = expression.args.get("unique_keys")
    2913        if unique_keys is not None:
    2914            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2915        else:
    2916            unique_keys = ""
    2917
    2918        return_type = self.sql(expression, "return_type")
    2919        return_type = f" RETURNING {return_type}" if return_type else ""
    2920        encoding = self.sql(expression, "encoding")
    2921        encoding = f" ENCODING {encoding}" if encoding else ""
    2922
    2923        return self.func(
    2924            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2925            *expression.expressions,
    2926            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2927        )

def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str: View Source

    2929    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
    2930        return self.jsonobject_sql(expression)

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

    2932    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2933        null_handling = expression.args.get("null_handling")
    2934        null_handling = f" {null_handling}" if null_handling else ""
    2935        return_type = self.sql(expression, "return_type")
    2936        return_type = f" RETURNING {return_type}" if return_type else ""
    2937        strict = " STRICT" if expression.args.get("strict") else ""
    2938        return self.func(
    2939            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2940        )

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

    2942    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2943        this = self.sql(expression, "this")
    2944        order = self.sql(expression, "order")
    2945        null_handling = expression.args.get("null_handling")
    2946        null_handling = f" {null_handling}" if null_handling else ""
    2947        return_type = self.sql(expression, "return_type")
    2948        return_type = f" RETURNING {return_type}" if return_type else ""
    2949        strict = " STRICT" if expression.args.get("strict") else ""
    2950        return self.func(
    2951            "JSON_ARRAYAGG",
    2952            this,
    2953            suffix=f"{order}{null_handling}{return_type}{strict})",
    2954        )

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

    2956    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2957        path = self.sql(expression, "path")
    2958        path = f" PATH {path}" if path else ""
    2959        nested_schema = self.sql(expression, "nested_schema")
    2960
    2961        if nested_schema:
    2962            return f"NESTED{path} {nested_schema}"
    2963
    2964        this = self.sql(expression, "this")
    2965        kind = self.sql(expression, "kind")
    2966        kind = f" {kind}" if kind else ""
    2967        return f"{this}{kind}{path}"

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

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

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

    2972    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2973        this = self.sql(expression, "this")
    2974        path = self.sql(expression, "path")
    2975        path = f", {path}" if path else ""
    2976        error_handling = expression.args.get("error_handling")
    2977        error_handling = f" {error_handling}" if error_handling else ""
    2978        empty_handling = expression.args.get("empty_handling")
    2979        empty_handling = f" {empty_handling}" if empty_handling else ""
    2980        schema = self.sql(expression, "schema")
    2981        return self.func(
    2982            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2983        )

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

    2985    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2986        this = self.sql(expression, "this")
    2987        kind = self.sql(expression, "kind")
    2988        path = self.sql(expression, "path")
    2989        path = f" {path}" if path else ""
    2990        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2991        return f"{this} {kind}{path}{as_json}"

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

    2993    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2994        this = self.sql(expression, "this")
    2995        path = self.sql(expression, "path")
    2996        path = f", {path}" if path else ""
    2997        expressions = self.expressions(expression)
    2998        with_ = (
    2999            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    3000            if expressions
    3001            else ""
    3002        )
    3003        return f"OPENJSON({this}{path}){with_}"

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

    3005    def in_sql(self, expression: exp.In) -> str:
    3006        query = expression.args.get("query")
    3007        unnest = expression.args.get("unnest")
    3008        field = expression.args.get("field")
    3009        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    3010
    3011        if query:
    3012            in_sql = self.sql(query)
    3013        elif unnest:
    3014            in_sql = self.in_unnest_op(unnest)
    3015        elif field:
    3016            in_sql = self.sql(field)
    3017        else:
    3018            in_sql = f"({self.expressions(expression, flat=True)})"
    3019
    3020        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    3025    def interval_sql(self, expression: exp.Interval) -> str:
    3026        unit = self.sql(expression, "unit")
    3027        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    3028            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    3029        unit = f" {unit}" if unit else ""
    3030
    3031        if self.SINGLE_STRING_INTERVAL:
    3032            this = expression.this.name if expression.this else ""
    3033            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    3034
    3035        this = self.sql(expression, "this")
    3036        if this:
    3037            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    3038            this = f" {this}" if unwrapped else f" ({this})"
    3039
    3040        return f"INTERVAL{this}{unit}"

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

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

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

    3045    def reference_sql(self, expression: exp.Reference) -> str:
    3046        this = self.sql(expression, "this")
    3047        expressions = self.expressions(expression, flat=True)
    3048        expressions = f"({expressions})" if expressions else ""
    3049        options = self.expressions(expression, key="options", flat=True, sep=" ")
    3050        options = f" {options}" if options else ""
    3051        return f"REFERENCES {this}{expressions}{options}"

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

    3053    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    3054        # We don't normalize qualified functions such as a.b.foo(), because they can be case-sensitive
    3055        parent = expression.parent
    3056        is_qualified = isinstance(parent, exp.Dot) and expression is parent.expression
    3057        return self.func(
    3058            self.sql(expression, "this"), *expression.expressions, normalize=not is_qualified
    3059        )

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

    3061    def paren_sql(self, expression: exp.Paren) -> str:
    3062        sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    3063        return f"({sql}{self.seg(')', sep='')}"

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

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

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

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

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

    3074    def alias_sql(self, expression: exp.Alias) -> str:
    3075        alias = self.sql(expression, "alias")
    3076        alias = f" AS {alias}" if alias else ""
    3077        return f"{self.sql(expression, 'this')}{alias}"

def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str: View Source

    3079    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    3080        alias = expression.args["alias"]
    3081
    3082        identifier_alias = isinstance(alias, exp.Identifier)
    3083        literal_alias = isinstance(alias, exp.Literal)
    3084
    3085        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3086            alias.replace(exp.Literal.string(alias.output_name))
    3087        elif not identifier_alias and literal_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    3088            alias.replace(exp.to_identifier(alias.output_name))
    3089
    3090        return self.alias_sql(expression)

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

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

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

    3095    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    3096        this = self.sql(expression, "this")
    3097        index = self.sql(expression, "expression")
    3098        return f"{this} AT {index}"

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

    3100    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    3101        this = self.sql(expression, "this")
    3102        zone = self.sql(expression, "zone")
    3103        return f"{this} AT TIME ZONE {zone}"

def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str: View Source

    3105    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    3106        this = self.sql(expression, "this")
    3107        zone = self.sql(expression, "zone")
    3108        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

def and_sql( self, expression: sqlglot.expressions.And, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3113    def and_sql(
    3114        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    3115    ) -> str:
    3116        return self.connector_sql(expression, "AND", stack)

def or_sql( self, expression: sqlglot.expressions.Or, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3118    def or_sql(
    3119        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    3120    ) -> str:
    3121        return self.connector_sql(expression, "OR", stack)

def xor_sql( self, expression: sqlglot.expressions.Xor, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3123    def xor_sql(
    3124        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    3125    ) -> str:
    3126        return self.connector_sql(expression, "XOR", stack)

def connector_sql( self, expression: sqlglot.expressions.Connector, op: str, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str: View Source

    3128    def connector_sql(
    3129        self,
    3130        expression: exp.Connector,
    3131        op: str,
    3132        stack: t.Optional[t.List[str | exp.Expression]] = None,
    3133    ) -> str:
    3134        if stack is not None:
    3135            if expression.expressions:
    3136                stack.append(self.expressions(expression, sep=f" {op} "))
    3137            else:
    3138                stack.append(expression.right)
    3139                if expression.comments and self.comments:
    3140                    for comment in expression.comments:
    3141                        if comment:
    3142                            op += f" /*{self.pad_comment(comment)}*/"
    3143                stack.extend((op, expression.left))
    3144            return op
    3145
    3146        stack = [expression]
    3147        sqls: t.List[str] = []
    3148        ops = set()
    3149
    3150        while stack:
    3151            node = stack.pop()
    3152            if isinstance(node, exp.Connector):
    3153                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    3154            else:
    3155                sql = self.sql(node)
    3156                if sqls and sqls[-1] in ops:
    3157                    sqls[-1] += f" {sql}"
    3158                else:
    3159                    sqls.append(sql)
    3160
    3161        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    3162        return sep.join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    3182    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    3183        format_sql = self.sql(expression, "format")
    3184        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    3185        to_sql = self.sql(expression, "to")
    3186        to_sql = f" {to_sql}" if to_sql else ""
    3187        action = self.sql(expression, "action")
    3188        action = f" {action}" if action else ""
    3189        default = self.sql(expression, "default")
    3190        default = f" DEFAULT {default} ON CONVERSION ERROR" if default else ""
    3191        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{default}{format_sql}{action})"

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

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

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

    3197    def collate_sql(self, expression: exp.Collate) -> str:
    3198        if self.COLLATE_IS_FUNC:
    3199            return self.function_fallback_sql(expression)
    3200        return self.binary(expression, "COLLATE")

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

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

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

    3205    def comment_sql(self, expression: exp.Comment) -> str:
    3206        this = self.sql(expression, "this")
    3207        kind = expression.args["kind"]
    3208        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    3209        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3210        expression_sql = self.sql(expression, "expression")
    3211        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"

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

    3213    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3214        this = self.sql(expression, "this")
    3215        delete = " DELETE" if expression.args.get("delete") else ""
    3216        recompress = self.sql(expression, "recompress")
    3217        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3218        to_disk = self.sql(expression, "to_disk")
    3219        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3220        to_volume = self.sql(expression, "to_volume")
    3221        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3222        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    3224    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3225        where = self.sql(expression, "where")
    3226        group = self.sql(expression, "group")
    3227        aggregates = self.expressions(expression, key="aggregates")
    3228        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3229
    3230        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3231            return f"TTL {self.expressions(expression, flat=True)}"
    3232
    3233        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    3238    def commit_sql(self, expression: exp.Commit) -> str:
    3239        chain = expression.args.get("chain")
    3240        if chain is not None:
    3241            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3242
    3243        return f"COMMIT{chain or ''}"

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

    3245    def rollback_sql(self, expression: exp.Rollback) -> str:
    3246        savepoint = expression.args.get("savepoint")
    3247        savepoint = f" TO {savepoint}" if savepoint else ""
    3248        return f"ROLLBACK{savepoint}"

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

    3250    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3251        this = self.sql(expression, "this")
    3252
    3253        dtype = self.sql(expression, "dtype")
    3254        if dtype:
    3255            collate = self.sql(expression, "collate")
    3256            collate = f" COLLATE {collate}" if collate else ""
    3257            using = self.sql(expression, "using")
    3258            using = f" USING {using}" if using else ""
    3259            return f"ALTER COLUMN {this} {self.ALTER_SET_TYPE} {dtype}{collate}{using}"
    3260
    3261        default = self.sql(expression, "default")
    3262        if default:
    3263            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3264
    3265        comment = self.sql(expression, "comment")
    3266        if comment:
    3267            return f"ALTER COLUMN {this} COMMENT {comment}"
    3268
    3269        allow_null = expression.args.get("allow_null")
    3270        drop = expression.args.get("drop")
    3271
    3272        if not drop and not allow_null:
    3273            self.unsupported("Unsupported ALTER COLUMN syntax")
    3274
    3275        if allow_null is not None:
    3276            keyword = "DROP" if drop else "SET"
    3277            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3278
    3279        return f"ALTER COLUMN {this} DROP DEFAULT"

def alterdiststyle_sql(self, expression: sqlglot.expressions.AlterDistStyle) -> str: View Source

    3281    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3282        this = self.sql(expression, "this")
    3283        if not isinstance(expression.this, exp.Var):
    3284            this = f"KEY DISTKEY {this}"
    3285        return f"ALTER DISTSTYLE {this}"

def altersortkey_sql(self, expression: sqlglot.expressions.AlterSortKey) -> str: View Source

    3287    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3288        compound = " COMPOUND" if expression.args.get("compound") else ""
    3289        this = self.sql(expression, "this")
    3290        expressions = self.expressions(expression, flat=True)
    3291        expressions = f"({expressions})" if expressions else ""
    3292        return f"ALTER{compound} SORTKEY {this or expressions}"

def alterrename_sql(self, expression: sqlglot.expressions.AlterRename) -> str: View Source

    3294    def alterrename_sql(self, expression: exp.AlterRename) -> str:
    3295        if not self.RENAME_TABLE_WITH_DB:
    3296            # Remove db from tables
    3297            expression = expression.transform(
    3298                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3299            ).assert_is(exp.AlterRename)
    3300        this = self.sql(expression, "this")
    3301        return f"RENAME TO {this}"

def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str: View Source

    3303    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3304        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3305        old_column = self.sql(expression, "this")
    3306        new_column = self.sql(expression, "to")
    3307        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

def alterset_sql(self, expression: sqlglot.expressions.AlterSet) -> str: View Source

    3309    def alterset_sql(self, expression: exp.AlterSet) -> str:
    3310        exprs = self.expressions(expression, flat=True)
    3311        return f"SET {exprs}"

def alter_sql(self, expression: sqlglot.expressions.Alter) -> str: View Source

    3313    def alter_sql(self, expression: exp.Alter) -> str:
    3314        actions = expression.args["actions"]
    3315
    3316        if isinstance(actions[0], exp.ColumnDef):
    3317            actions = self.add_column_sql(expression)
    3318        elif isinstance(actions[0], exp.Schema):
    3319            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3320        elif isinstance(actions[0], exp.Delete):
    3321            actions = self.expressions(expression, key="actions", flat=True)
    3322        elif isinstance(actions[0], exp.Query):
    3323            actions = "AS " + self.expressions(expression, key="actions")
    3324        else:
    3325            actions = self.expressions(expression, key="actions", flat=True)
    3326
    3327        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3328        on_cluster = self.sql(expression, "cluster")
    3329        on_cluster = f" {on_cluster}" if on_cluster else ""
    3330        only = " ONLY" if expression.args.get("only") else ""
    3331        options = self.expressions(expression, key="options")
    3332        options = f", {options}" if options else ""
    3333        kind = self.sql(expression, "kind")
    3334        not_valid = " NOT VALID" if expression.args.get("not_valid") else ""
    3335
    3336        return f"ALTER {kind}{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{not_valid}{options}"

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

    3338    def add_column_sql(self, expression: exp.Alter) -> str:
    3339        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3340            return self.expressions(
    3341                expression,
    3342                key="actions",
    3343                prefix="ADD COLUMN ",
    3344                skip_first=True,
    3345            )
    3346        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    3348    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3349        expressions = self.expressions(expression)
    3350        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3351        return f"DROP{exists}{expressions}"

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

    3353    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
    3354        return f"ADD {self.expressions(expression)}"

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

    3356    def distinct_sql(self, expression: exp.Distinct) -> str:
    3357        this = self.expressions(expression, flat=True)
    3358
    3359        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3360            case = exp.case()
    3361            for arg in expression.expressions:
    3362                case = case.when(arg.is_(exp.null()), exp.null())
    3363            this = self.sql(case.else_(f"({this})"))
    3364
    3365        this = f" {this}" if this else ""
    3366
    3367        on = self.sql(expression, "on")
    3368        on = f" ON {on}" if on else ""
    3369        return f"DISTINCT{this}{on}"

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

    3371    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
    3372        return self._embed_ignore_nulls(expression, "IGNORE NULLS")

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

    3374    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
    3375        return self._embed_ignore_nulls(expression, "RESPECT NULLS")

def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str: View Source

    3377    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3378        this_sql = self.sql(expression, "this")
    3379        expression_sql = self.sql(expression, "expression")
    3380        kind = "MAX" if expression.args.get("max") else "MIN"
    3381        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3383    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3384        return self.sql(
    3385            exp.Cast(
    3386                this=exp.Div(this=expression.this, expression=expression.expression),
    3387                to=exp.DataType(this=exp.DataType.Type.INT),
    3388            )
    3389        )

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

    3391    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3392        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3393            return self.func(
    3394                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3395            )
    3396        return self.binary(expression, "||")

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

    3398    def div_sql(self, expression: exp.Div) -> str:
    3399        l, r = expression.left, expression.right
    3400
    3401        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3402            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3403
    3404        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3405            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3406                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3407
    3408        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3409            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3410                return self.sql(
    3411                    exp.cast(
    3412                        l / r,
    3413                        to=exp.DataType.Type.BIGINT,
    3414                    )
    3415                )
    3416
    3417        return self.binary(expression, "/")

def safedivide_sql(self, expression: sqlglot.expressions.SafeDivide) -> str: View Source

    3419    def safedivide_sql(self, expression: exp.SafeDivide) -> str:
    3420        n = exp._wrap(expression.this, exp.Binary)
    3421        d = exp._wrap(expression.expression, exp.Binary)
    3422        return self.sql(exp.If(this=d.neq(0), true=n / d, false=exp.Null()))

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3457    def is_sql(self, expression: exp.Is) -> str:
    3458        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3459            return self.sql(
    3460                expression.this if expression.expression.this else exp.not_(expression.this)
    3461            )
    3462        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

def try_sql(self, expression: sqlglot.expressions.Try) -> str: View Source

    3503    def try_sql(self, expression: exp.Try) -> str:
    3504        if not self.TRY_SUPPORTED:
    3505            self.unsupported("Unsupported TRY function")
    3506            return self.sql(expression, "this")
    3507
    3508        return self.func("TRY", expression.this)

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

    3510    def log_sql(self, expression: exp.Log) -> str:
    3511        this = expression.this
    3512        expr = expression.expression
    3513
    3514        if self.dialect.LOG_BASE_FIRST is False:
    3515            this, expr = expr, this
    3516        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3517            if this.name in ("2", "10"):
    3518                return self.func(f"LOG{this.name}", expr)
    3519
    3520            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3521
    3522        return self.func("LOG", this, expr)

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

    3524    def use_sql(self, expression: exp.Use) -> str:
    3525        kind = self.sql(expression, "kind")
    3526        kind = f" {kind}" if kind else ""
    3527        this = self.sql(expression, "this")
    3528        this = f" {this}" if this else ""
    3529        return f"USE{kind}{this}"

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

    3531    def binary(self, expression: exp.Binary, op: str) -> str:
    3532        sqls: t.List[str] = []
    3533        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    3534        binary_type = type(expression)
    3535
    3536        while stack:
    3537            node = stack.pop()
    3538
    3539            if type(node) is binary_type:
    3540                op_func = node.args.get("operator")
    3541                if op_func:
    3542                    op = f"OPERATOR({self.sql(op_func)})"
    3543
    3544                stack.append(node.right)
    3545                stack.append(f" {self.maybe_comment(op, comments=node.comments)} ")
    3546                stack.append(node.left)
    3547            else:
    3548                sqls.append(self.sql(node))
    3549
    3550        return "".join(sqls)

def ceil_floor( self, expression: sqlglot.expressions.Ceil | sqlglot.expressions.Floor) -> str: View Source

    3552    def ceil_floor(self, expression: exp.Ceil | exp.Floor) -> str:
    3553        to_clause = self.sql(expression, "to")
    3554        if to_clause:
    3555            return f"{expression.sql_name()}({self.sql(expression, 'this')} TO {to_clause})"
    3556
    3557        return self.function_fallback_sql(expression)

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

    3559    def function_fallback_sql(self, expression: exp.Func) -> str:
    3560        args = []
    3561
    3562        for key in expression.arg_types:
    3563            arg_value = expression.args.get(key)
    3564
    3565            if isinstance(arg_value, list):
    3566                for value in arg_value:
    3567                    args.append(value)
    3568            elif arg_value is not None:
    3569                args.append(arg_value)
    3570
    3571        if self.dialect.PRESERVE_ORIGINAL_NAMES:
    3572            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3573        else:
    3574            name = expression.sql_name()
    3575
    3576        return self.func(name, *args)

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

    3578    def func(
    3579        self,
    3580        name: str,
    3581        *args: t.Optional[exp.Expression | str],
    3582        prefix: str = "(",
    3583        suffix: str = ")",
    3584        normalize: bool = True,
    3585    ) -> str:
    3586        name = self.normalize_func(name) if normalize else name
    3587        return f"{name}{prefix}{self.format_args(*args)}{suffix}"

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

    3589    def format_args(self, *args: t.Optional[str | exp.Expression], sep: str = ", ") -> str:
    3590        arg_sqls = tuple(
    3591            self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool)
    3592        )
    3593        if self.pretty and self.too_wide(arg_sqls):
    3594            return self.indent(
    3595                "\n" + f"{sep.strip()}\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True
    3596            )
    3597        return sep.join(arg_sqls)

def too_wide(self, args: Iterable) -> bool: View Source

    3599    def too_wide(self, args: t.Iterable) -> bool:
    3600        return sum(len(arg) for arg in args) > self.max_text_width

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

    3602    def format_time(
    3603        self,
    3604        expression: exp.Expression,
    3605        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3606        inverse_time_trie: t.Optional[t.Dict] = None,
    3607    ) -> t.Optional[str]:
    3608        return format_time(
    3609            self.sql(expression, "format"),
    3610            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3611            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3612        )

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

    3614    def expressions(
    3615        self,
    3616        expression: t.Optional[exp.Expression] = None,
    3617        key: t.Optional[str] = None,
    3618        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3619        flat: bool = False,
    3620        indent: bool = True,
    3621        skip_first: bool = False,
    3622        skip_last: bool = False,
    3623        sep: str = ", ",
    3624        prefix: str = "",
    3625        dynamic: bool = False,
    3626        new_line: bool = False,
    3627    ) -> str:
    3628        expressions = expression.args.get(key or "expressions") if expression else sqls
    3629
    3630        if not expressions:
    3631            return ""
    3632
    3633        if flat:
    3634            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3635
    3636        num_sqls = len(expressions)
    3637        result_sqls = []
    3638
    3639        for i, e in enumerate(expressions):
    3640            sql = self.sql(e, comment=False)
    3641            if not sql:
    3642                continue
    3643
    3644            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3645
    3646            if self.pretty:
    3647                if self.leading_comma:
    3648                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3649                else:
    3650                    result_sqls.append(
    3651                        f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}"
    3652                    )
    3653            else:
    3654                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3655
    3656        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3657            if new_line:
    3658                result_sqls.insert(0, "")
    3659                result_sqls.append("")
    3660            result_sql = "\n".join(s.rstrip() for s in result_sqls)
    3661        else:
    3662            result_sql = "".join(result_sqls)
    3663
    3664        return (
    3665            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3666            if indent
    3667            else result_sql
    3668        )

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

    3670    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3671        flat = flat or isinstance(expression.parent, exp.Properties)
    3672        expressions_sql = self.expressions(expression, flat=flat)
    3673        if flat:
    3674            return f"{op} {expressions_sql}"
    3675        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3677    def naked_property(self, expression: exp.Property) -> str:
    3678        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3679        if not property_name:
    3680            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3681        return f"{property_name} {self.sql(expression, 'this')}"

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

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

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

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

    3689    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3690        this = self.sql(expression, "this")
    3691        expressions = self.no_identify(self.expressions, expression)
    3692        expressions = (
    3693            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3694        )
    3695        return f"{this}{expressions}" if expressions.strip() != "" else this

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

    3697    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3698        this = self.sql(expression, "this")
    3699        expressions = self.expressions(expression, flat=True)
    3700        return f"{this}({expressions})"

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

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

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

    3705    def when_sql(self, expression: exp.When) -> str:
    3706        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3707        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3708        condition = self.sql(expression, "condition")
    3709        condition = f" AND {condition}" if condition else ""
    3710
    3711        then_expression = expression.args.get("then")
    3712        if isinstance(then_expression, exp.Insert):
    3713            this = self.sql(then_expression, "this")
    3714            this = f"INSERT {this}" if this else "INSERT"
    3715            then = self.sql(then_expression, "expression")
    3716            then = f"{this} VALUES {then}" if then else this
    3717        elif isinstance(then_expression, exp.Update):
    3718            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3719                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3720            else:
    3721                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3722        else:
    3723            then = self.sql(then_expression)
    3724        return f"WHEN {matched}{source}{condition} THEN {then}"

def whens_sql(self, expression: sqlglot.expressions.Whens) -> str: View Source

    3726    def whens_sql(self, expression: exp.Whens) -> str:
    3727        return self.expressions(expression, sep=" ", indent=False)

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

    3729    def merge_sql(self, expression: exp.Merge) -> str:
    3730        table = expression.this
    3731        table_alias = ""
    3732
    3733        hints = table.args.get("hints")
    3734        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3735            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3736            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3737
    3738        this = self.sql(table)
    3739        using = f"USING {self.sql(expression, 'using')}"
    3740        on = f"ON {self.sql(expression, 'on')}"
    3741        whens = self.sql(expression, "whens")
    3742
    3743        returning = self.sql(expression, "returning")
    3744        if returning:
    3745            whens = f"{whens}{returning}"
    3746
    3747        sep = self.sep()
    3748
    3749        return self.prepend_ctes(
    3750            expression,
    3751            f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{whens}",
    3752        )

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

    3754    @unsupported_args("format")
    3755    def tochar_sql(self, expression: exp.ToChar) -> str:
    3756        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))

def tonumber_sql(self, expression: sqlglot.expressions.ToNumber) -> str: View Source

    3758    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3759        if not self.SUPPORTS_TO_NUMBER:
    3760            self.unsupported("Unsupported TO_NUMBER function")
    3761            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3762
    3763        fmt = expression.args.get("format")
    3764        if not fmt:
    3765            self.unsupported("Conversion format is required for TO_NUMBER")
    3766            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3767
    3768        return self.func("TO_NUMBER", expression.this, fmt)

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

    3770    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3771        this = self.sql(expression, "this")
    3772        kind = self.sql(expression, "kind")
    3773        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3774        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3775        return f"{this}({kind}{args})"

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

    3777    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3778        this = self.sql(expression, "this")
    3779        max = self.sql(expression, "max")
    3780        min = self.sql(expression, "min")
    3781        return f"{this}(MIN {min} MAX {max})"

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

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

def duplicatekeyproperty_sql(self, expression: sqlglot.expressions.DuplicateKeyProperty) -> str: View Source

    3786    def duplicatekeyproperty_sql(self, expression: exp.DuplicateKeyProperty) -> str:
    3787        return f"DUPLICATE KEY ({self.expressions(expression, flat=True)})"

def uniquekeyproperty_sql(self, expression: sqlglot.expressions.UniqueKeyProperty) -> str: View Source

    3790    def uniquekeyproperty_sql(self, expression: exp.UniqueKeyProperty) -> str:
    3791        return f"UNIQUE KEY ({self.expressions(expression, flat=True)})"

def distributedbyproperty_sql(self, expression: sqlglot.expressions.DistributedByProperty) -> str: View Source

    3794    def distributedbyproperty_sql(self, expression: exp.DistributedByProperty) -> str:
    3795        expressions = self.expressions(expression, flat=True)
    3796        expressions = f" {self.wrap(expressions)}" if expressions else ""
    3797        buckets = self.sql(expression, "buckets")
    3798        kind = self.sql(expression, "kind")
    3799        buckets = f" BUCKETS {buckets}" if buckets else ""
    3800        order = self.sql(expression, "order")
    3801        return f"DISTRIBUTED BY {kind}{expressions}{buckets}{order}"

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

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

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

    3806    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3807        expressions = self.expressions(expression, key="expressions", flat=True)
    3808        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3809        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3810        buckets = self.sql(expression, "buckets")
    3811        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3813    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3814        this = self.sql(expression, "this")
    3815        having = self.sql(expression, "having")
    3816
    3817        if having:
    3818            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3819
    3820        return self.func("ANY_VALUE", this)

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

    3822    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3823        transform = self.func("TRANSFORM", *expression.expressions)
    3824        row_format_before = self.sql(expression, "row_format_before")
    3825        row_format_before = f" {row_format_before}" if row_format_before else ""
    3826        record_writer = self.sql(expression, "record_writer")
    3827        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3828        using = f" USING {self.sql(expression, 'command_script')}"
    3829        schema = self.sql(expression, "schema")
    3830        schema = f" AS {schema}" if schema else ""
    3831        row_format_after = self.sql(expression, "row_format_after")
    3832        row_format_after = f" {row_format_after}" if row_format_after else ""
    3833        record_reader = self.sql(expression, "record_reader")
    3834        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3835        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"

def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str: View Source

    3837    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3838        key_block_size = self.sql(expression, "key_block_size")
    3839        if key_block_size:
    3840            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3841
    3842        using = self.sql(expression, "using")
    3843        if using:
    3844            return f"USING {using}"
    3845
    3846        parser = self.sql(expression, "parser")
    3847        if parser:
    3848            return f"WITH PARSER {parser}"
    3849
    3850        comment = self.sql(expression, "comment")
    3851        if comment:
    3852            return f"COMMENT {comment}"
    3853
    3854        visible = expression.args.get("visible")
    3855        if visible is not None:
    3856            return "VISIBLE" if visible else "INVISIBLE"
    3857
    3858        engine_attr = self.sql(expression, "engine_attr")
    3859        if engine_attr:
    3860            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3861
    3862        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3863        if secondary_engine_attr:
    3864            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3865
    3866        self.unsupported("Unsupported index constraint option.")
    3867        return ""

def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str: View Source

    3869    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
    3870        enforced = " ENFORCED" if expression.args.get("enforced") else ""
    3871        return f"CHECK ({self.sql(expression, 'this')}){enforced}"

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

    3873    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3874        kind = self.sql(expression, "kind")
    3875        kind = f"{kind} INDEX" if kind else "INDEX"
    3876        this = self.sql(expression, "this")
    3877        this = f" {this}" if this else ""
    3878        index_type = self.sql(expression, "index_type")
    3879        index_type = f" USING {index_type}" if index_type else ""
    3880        expressions = self.expressions(expression, flat=True)
    3881        expressions = f" ({expressions})" if expressions else ""
    3882        options = self.expressions(expression, key="options", sep=" ")
    3883        options = f" {options}" if options else ""
    3884        return f"{kind}{this}{index_type}{expressions}{options}"

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

    3886    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3887        if self.NVL2_SUPPORTED:
    3888            return self.function_fallback_sql(expression)
    3889
    3890        case = exp.Case().when(
    3891            expression.this.is_(exp.null()).not_(copy=False),
    3892            expression.args["true"],
    3893            copy=False,
    3894        )
    3895        else_cond = expression.args.get("false")
    3896        if else_cond:
    3897            case.else_(else_cond, copy=False)
    3898
    3899        return self.sql(case)

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

    3901    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3902        this = self.sql(expression, "this")
    3903        expr = self.sql(expression, "expression")
    3904        iterator = self.sql(expression, "iterator")
    3905        condition = self.sql(expression, "condition")
    3906        condition = f" IF {condition}" if condition else ""
    3907        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3915    def predict_sql(self, expression: exp.Predict) -> str:
    3916        model = self.sql(expression, "this")
    3917        model = f"MODEL {model}"
    3918        table = self.sql(expression, "expression")
    3919        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3920        parameters = self.sql(expression, "params_struct")
    3921        return self.func("PREDICT", model, table, parameters or None)

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

    3923    def forin_sql(self, expression: exp.ForIn) -> str:
    3924        this = self.sql(expression, "this")
    3925        expression_sql = self.sql(expression, "expression")
    3926        return f"FOR {this} DO {expression_sql}"

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

    3928    def refresh_sql(self, expression: exp.Refresh) -> str:
    3929        this = self.sql(expression, "this")
    3930        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3931        return f"REFRESH {table}{this}"

def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str: View Source

    3933    def toarray_sql(self, expression: exp.ToArray) -> str:
    3934        arg = expression.this
    3935        if not arg.type:
    3936            from sqlglot.optimizer.annotate_types import annotate_types
    3937
    3938            arg = annotate_types(arg)
    3939
    3940        if arg.is_type(exp.DataType.Type.ARRAY):
    3941            return self.sql(arg)
    3942
    3943        cond_for_null = arg.is_(exp.null())
    3944        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))

def tsordstotime_sql(self, expression: sqlglot.expressions.TsOrDsToTime) -> str: View Source

    3946    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3947        this = expression.this
    3948        time_format = self.format_time(expression)
    3949
    3950        if time_format:
    3951            return self.sql(
    3952                exp.cast(
    3953                    exp.StrToTime(this=this, format=expression.args["format"]),
    3954                    exp.DataType.Type.TIME,
    3955                )
    3956            )
    3957
    3958        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3959            return self.sql(this)
    3960
    3961        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

def tsordstotimestamp_sql(self, expression: sqlglot.expressions.TsOrDsToTimestamp) -> str: View Source

    3963    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3964        this = expression.this
    3965        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3966            return self.sql(this)
    3967
    3968        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP, dialect=self.dialect))

def tsordstodatetime_sql(self, expression: sqlglot.expressions.TsOrDsToDatetime) -> str: View Source

    3970    def tsordstodatetime_sql(self, expression: exp.TsOrDsToDatetime) -> str:
    3971        this = expression.this
    3972        if isinstance(this, exp.TsOrDsToDatetime) or this.is_type(exp.DataType.Type.DATETIME):
    3973            return self.sql(this)
    3974
    3975        return self.sql(exp.cast(this, exp.DataType.Type.DATETIME, dialect=self.dialect))

def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str: View Source

    3977    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3978        this = expression.this
    3979        time_format = self.format_time(expression)
    3980
    3981        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3982            return self.sql(
    3983                exp.cast(
    3984                    exp.StrToTime(this=this, format=expression.args["format"]),
    3985                    exp.DataType.Type.DATE,
    3986                )
    3987            )
    3988
    3989        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3990            return self.sql(this)
    3991
    3992        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str: View Source

    3994    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3995        return self.sql(
    3996            exp.func(
    3997                "DATEDIFF",
    3998                expression.this,
    3999                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    4000                "day",
    4001            )
    4002        )

def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str: View Source

    4004    def lastday_sql(self, expression: exp.LastDay) -> str:
    4005        if self.LAST_DAY_SUPPORTS_DATE_PART:
    4006            return self.function_fallback_sql(expression)
    4007
    4008        unit = expression.text("unit")
    4009        if unit and unit != "MONTH":
    4010            self.unsupported("Date parts are not supported in LAST_DAY.")
    4011
    4012        return self.func("LAST_DAY", expression.this)

def dateadd_sql(self, expression: sqlglot.expressions.DateAdd) -> str: View Source

    4014    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    4015        from sqlglot.dialects.dialect import unit_to_str
    4016
    4017        return self.func(
    4018            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    4019        )

def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str: View Source

    4021    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    4022        if self.CAN_IMPLEMENT_ARRAY_ANY:
    4023            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    4024            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    4025            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    4026            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    4027
    4028        from sqlglot.dialects import Dialect
    4029
    4030        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    4031        if self.dialect.__class__ != Dialect:
    4032            self.unsupported("ARRAY_ANY is unsupported")
    4033
    4034        return self.function_fallback_sql(expression)

def struct_sql(self, expression: sqlglot.expressions.Struct) -> str: View Source

    4036    def struct_sql(self, expression: exp.Struct) -> str:
    4037        expression.set(
    4038            "expressions",
    4039            [
    4040                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    4041                if isinstance(e, exp.PropertyEQ)
    4042                else e
    4043                for e in expression.expressions
    4044            ],
    4045        )
    4046
    4047        return self.function_fallback_sql(expression)

def partitionrange_sql(self, expression: sqlglot.expressions.PartitionRange) -> str: View Source

    4049    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    4050        low = self.sql(expression, "this")
    4051        high = self.sql(expression, "expression")
    4052
    4053        return f"{low} TO {high}"

def truncatetable_sql(self, expression: sqlglot.expressions.TruncateTable) -> str: View Source

    4055    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    4056        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    4057        tables = f" {self.expressions(expression)}"
    4058
    4059        exists = " IF EXISTS" if expression.args.get("exists") else ""
    4060
    4061        on_cluster = self.sql(expression, "cluster")
    4062        on_cluster = f" {on_cluster}" if on_cluster else ""
    4063
    4064        identity = self.sql(expression, "identity")
    4065        identity = f" {identity} IDENTITY" if identity else ""
    4066
    4067        option = self.sql(expression, "option")
    4068        option = f" {option}" if option else ""
    4069
    4070        partition = self.sql(expression, "partition")
    4071        partition = f" {partition}" if partition else ""
    4072
    4073        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

def convert_sql(self, expression: sqlglot.expressions.Convert) -> str: View Source

    4077    def convert_sql(self, expression: exp.Convert) -> str:
    4078        to = expression.this
    4079        value = expression.expression
    4080        style = expression.args.get("style")
    4081        safe = expression.args.get("safe")
    4082        strict = expression.args.get("strict")
    4083
    4084        if not to or not value:
    4085            return ""
    4086
    4087        # Retrieve length of datatype and override to default if not specified
    4088        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4089            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    4090
    4091        transformed: t.Optional[exp.Expression] = None
    4092        cast = exp.Cast if strict else exp.TryCast
    4093
    4094        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    4095        if isinstance(style, exp.Literal) and style.is_int:
    4096            from sqlglot.dialects.tsql import TSQL
    4097
    4098            style_value = style.name
    4099            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    4100            if not converted_style:
    4101                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    4102
    4103            fmt = exp.Literal.string(converted_style)
    4104
    4105            if to.this == exp.DataType.Type.DATE:
    4106                transformed = exp.StrToDate(this=value, format=fmt)
    4107            elif to.this in (exp.DataType.Type.DATETIME, exp.DataType.Type.DATETIME2):
    4108                transformed = exp.StrToTime(this=value, format=fmt)
    4109            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    4110                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    4111            elif to.this == exp.DataType.Type.TEXT:
    4112                transformed = exp.TimeToStr(this=value, format=fmt)
    4113
    4114        if not transformed:
    4115            transformed = cast(this=value, to=to, safe=safe)
    4116
    4117        return self.sql(transformed)

def copyparameter_sql(self, expression: sqlglot.expressions.CopyParameter) -> str: View Source

    4177    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    4178        option = self.sql(expression, "this")
    4179
    4180        if expression.expressions:
    4181            upper = option.upper()
    4182
    4183            # Snowflake FILE_FORMAT options are separated by whitespace
    4184            sep = " " if upper == "FILE_FORMAT" else ", "
    4185
    4186            # Databricks copy/format options do not set their list of values with EQ
    4187            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    4188            values = self.expressions(expression, flat=True, sep=sep)
    4189            return f"{option}{op}({values})"
    4190
    4191        value = self.sql(expression, "expression")
    4192
    4193        if not value:
    4194            return option
    4195
    4196        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    4197
    4198        return f"{option}{op}{value}"

def credentials_sql(self, expression: sqlglot.expressions.Credentials) -> str: View Source

    4200    def credentials_sql(self, expression: exp.Credentials) -> str:
    4201        cred_expr = expression.args.get("credentials")
    4202        if isinstance(cred_expr, exp.Literal):
    4203            # Redshift case: CREDENTIALS <string>
    4204            credentials = self.sql(expression, "credentials")
    4205            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    4206        else:
    4207            # Snowflake case: CREDENTIALS = (...)
    4208            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    4209            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    4210
    4211        storage = self.sql(expression, "storage")
    4212        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    4213
    4214        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    4215        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    4216
    4217        iam_role = self.sql(expression, "iam_role")
    4218        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    4219
    4220        region = self.sql(expression, "region")
    4221        region = f" REGION {region}" if region else ""
    4222
    4223        return f"{credentials}{storage}{encryption}{iam_role}{region}"

def copy_sql(self, expression: sqlglot.expressions.Copy) -> str: View Source

    4225    def copy_sql(self, expression: exp.Copy) -> str:
    4226        this = self.sql(expression, "this")
    4227        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    4228
    4229        credentials = self.sql(expression, "credentials")
    4230        credentials = self.seg(credentials) if credentials else ""
    4231        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    4232        files = self.expressions(expression, key="files", flat=True)
    4233
    4234        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    4235        params = self.expressions(
    4236            expression,
    4237            key="params",
    4238            sep=sep,
    4239            new_line=True,
    4240            skip_last=True,
    4241            skip_first=True,
    4242            indent=self.COPY_PARAMS_ARE_WRAPPED,
    4243        )
    4244
    4245        if params:
    4246            if self.COPY_PARAMS_ARE_WRAPPED:
    4247                params = f" WITH ({params})"
    4248            elif not self.pretty:
    4249                params = f" {params}"
    4250
    4251        return f"COPY{this}{kind} {files}{credentials}{params}"

def semicolon_sql(self, expression: sqlglot.expressions.Semicolon) -> str: View Source

    4253    def semicolon_sql(self, expression: exp.Semicolon) -> str:
    4254        return ""

def datadeletionproperty_sql(self, expression: sqlglot.expressions.DataDeletionProperty) -> str: View Source

    4256    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    4257        on_sql = "ON" if expression.args.get("on") else "OFF"
    4258        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    4259        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    4260        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    4261        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    4262
    4263        if filter_col or retention_period:
    4264            on_sql = self.func("ON", filter_col, retention_period)
    4265
    4266        return f"DATA_DELETION={on_sql}"

def maskingpolicycolumnconstraint_sql( self, expression: sqlglot.expressions.MaskingPolicyColumnConstraint) -> str: View Source

    4268    def maskingpolicycolumnconstraint_sql(
    4269        self, expression: exp.MaskingPolicyColumnConstraint
    4270    ) -> str:
    4271        this = self.sql(expression, "this")
    4272        expressions = self.expressions(expression, flat=True)
    4273        expressions = f" USING ({expressions})" if expressions else ""
    4274        return f"MASKING POLICY {this}{expressions}"

def gapfill_sql(self, expression: sqlglot.expressions.GapFill) -> str: View Source

    4276    def gapfill_sql(self, expression: exp.GapFill) -> str:
    4277        this = self.sql(expression, "this")
    4278        this = f"TABLE {this}"
    4279        return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"])

def scope_resolution(self, rhs: str, scope_name: str) -> str: View Source

    4281    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    4282        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)

def scoperesolution_sql(self, expression: sqlglot.expressions.ScopeResolution) -> str: View Source

    4284    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    4285        this = self.sql(expression, "this")
    4286        expr = expression.expression
    4287
    4288        if isinstance(expr, exp.Func):
    4289            # T-SQL's CLR functions are case sensitive
    4290            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4291        else:
    4292            expr = self.sql(expression, "expression")
    4293
    4294        return self.scope_resolution(expr, this)

def parsejson_sql(self, expression: sqlglot.expressions.ParseJSON) -> str: View Source

    4296    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4297        if self.PARSE_JSON_NAME is None:
    4298            return self.sql(expression.this)
    4299
    4300        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)

def rand_sql(self, expression: sqlglot.expressions.Rand) -> str: View Source

    4302    def rand_sql(self, expression: exp.Rand) -> str:
    4303        lower = self.sql(expression, "lower")
    4304        upper = self.sql(expression, "upper")
    4305
    4306        if lower and upper:
    4307            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4308        return self.func("RAND", expression.this)

def changes_sql(self, expression: sqlglot.expressions.Changes) -> str: View Source

    4310    def changes_sql(self, expression: exp.Changes) -> str:
    4311        information = self.sql(expression, "information")
    4312        information = f"INFORMATION => {information}"
    4313        at_before = self.sql(expression, "at_before")
    4314        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4315        end = self.sql(expression, "end")
    4316        end = f"{self.seg('')}{end}" if end else ""
    4317
    4318        return f"CHANGES ({information}){at_before}{end}"

def pad_sql(self, expression: sqlglot.expressions.Pad) -> str: View Source

    4320    def pad_sql(self, expression: exp.Pad) -> str:
    4321        prefix = "L" if expression.args.get("is_left") else "R"
    4322
    4323        fill_pattern = self.sql(expression, "fill_pattern") or None
    4324        if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED:
    4325            fill_pattern = "' '"
    4326
    4327        return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)

def summarize_sql(self, expression: sqlglot.expressions.Summarize) -> str: View Source

    4329    def summarize_sql(self, expression: exp.Summarize) -> str:
    4330        table = " TABLE" if expression.args.get("table") else ""
    4331        return f"SUMMARIZE{table} {self.sql(expression.this)}"

def explodinggenerateseries_sql(self, expression: sqlglot.expressions.ExplodingGenerateSeries) -> str: View Source

    4333    def explodinggenerateseries_sql(self, expression: exp.ExplodingGenerateSeries) -> str:
    4334        generate_series = exp.GenerateSeries(**expression.args)
    4335
    4336        parent = expression.parent
    4337        if isinstance(parent, (exp.Alias, exp.TableAlias)):
    4338            parent = parent.parent
    4339
    4340        if self.SUPPORTS_EXPLODING_PROJECTIONS and not isinstance(parent, (exp.Table, exp.Unnest)):
    4341            return self.sql(exp.Unnest(expressions=[generate_series]))
    4342
    4343        if isinstance(parent, exp.Select):
    4344            self.unsupported("GenerateSeries projection unnesting is not supported.")
    4345
    4346        return self.sql(generate_series)

def arrayconcat_sql( self, expression: sqlglot.expressions.ArrayConcat, name: str = 'ARRAY_CONCAT') -> str: View Source

    4348    def arrayconcat_sql(self, expression: exp.ArrayConcat, name: str = "ARRAY_CONCAT") -> str:
    4349        exprs = expression.expressions
    4350        if not self.ARRAY_CONCAT_IS_VAR_LEN:
    4351            rhs = reduce(lambda x, y: exp.ArrayConcat(this=x, expressions=[y]), exprs)
    4352        else:
    4353            rhs = self.expressions(expression)
    4354
    4355        return self.func(name, expression.this, rhs or None)

def converttimezone_sql(self, expression: sqlglot.expressions.ConvertTimezone) -> str: View Source

    4357    def converttimezone_sql(self, expression: exp.ConvertTimezone) -> str:
    4358        if self.SUPPORTS_CONVERT_TIMEZONE:
    4359            return self.function_fallback_sql(expression)
    4360
    4361        source_tz = expression.args.get("source_tz")
    4362        target_tz = expression.args.get("target_tz")
    4363        timestamp = expression.args.get("timestamp")
    4364
    4365        if source_tz and timestamp:
    4366            timestamp = exp.AtTimeZone(
    4367                this=exp.cast(timestamp, exp.DataType.Type.TIMESTAMPNTZ), zone=source_tz
    4368            )
    4369
    4370        expr = exp.AtTimeZone(this=timestamp, zone=target_tz)
    4371
    4372        return self.sql(expr)

def json_sql(self, expression: sqlglot.expressions.JSON) -> str: View Source

    4374    def json_sql(self, expression: exp.JSON) -> str:
    4375        this = self.sql(expression, "this")
    4376        this = f" {this}" if this else ""
    4377
    4378        _with = expression.args.get("with")
    4379
    4380        if _with is None:
    4381            with_sql = ""
    4382        elif not _with:
    4383            with_sql = " WITHOUT"
    4384        else:
    4385            with_sql = " WITH"
    4386
    4387        unique_sql = " UNIQUE KEYS" if expression.args.get("unique") else ""
    4388
    4389        return f"JSON{this}{with_sql}{unique_sql}"

def jsonvalue_sql(self, expression: sqlglot.expressions.JSONValue) -> str: View Source

    4391    def jsonvalue_sql(self, expression: exp.JSONValue) -> str:
    4392        def _generate_on_options(arg: t.Any) -> str:
    4393            return arg if isinstance(arg, str) else f"DEFAULT {self.sql(arg)}"
    4394
    4395        path = self.sql(expression, "path")
    4396        returning = self.sql(expression, "returning")
    4397        returning = f" RETURNING {returning}" if returning else ""
    4398
    4399        on_condition = self.sql(expression, "on_condition")
    4400        on_condition = f" {on_condition}" if on_condition else ""
    4401
    4402        return self.func("JSON_VALUE", expression.this, f"{path}{returning}{on_condition}")

def conditionalinsert_sql(self, expression: sqlglot.expressions.ConditionalInsert) -> str: View Source

    4404    def conditionalinsert_sql(self, expression: exp.ConditionalInsert) -> str:
    4405        else_ = "ELSE " if expression.args.get("else_") else ""
    4406        condition = self.sql(expression, "expression")
    4407        condition = f"WHEN {condition} THEN " if condition else else_
    4408        insert = self.sql(expression, "this")[len("INSERT") :].strip()
    4409        return f"{condition}{insert}"

def multitableinserts_sql(self, expression: sqlglot.expressions.MultitableInserts) -> str: View Source

    4411    def multitableinserts_sql(self, expression: exp.MultitableInserts) -> str:
    4412        kind = self.sql(expression, "kind")
    4413        expressions = self.seg(self.expressions(expression, sep=" "))
    4414        res = f"INSERT {kind}{expressions}{self.seg(self.sql(expression, 'source'))}"
    4415        return res

def oncondition_sql(self, expression: sqlglot.expressions.OnCondition) -> str: View Source

    4417    def oncondition_sql(self, expression: exp.OnCondition) -> str:
    4418        # Static options like "NULL ON ERROR" are stored as strings, in contrast to "DEFAULT <expr> ON ERROR"
    4419        empty = expression.args.get("empty")
    4420        empty = (
    4421            f"DEFAULT {empty} ON EMPTY"
    4422            if isinstance(empty, exp.Expression)
    4423            else self.sql(expression, "empty")
    4424        )
    4425
    4426        error = expression.args.get("error")
    4427        error = (
    4428            f"DEFAULT {error} ON ERROR"
    4429            if isinstance(error, exp.Expression)
    4430            else self.sql(expression, "error")
    4431        )
    4432
    4433        if error and empty:
    4434            error = (
    4435                f"{empty} {error}"
    4436                if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR
    4437                else f"{error} {empty}"
    4438            )
    4439            empty = ""
    4440
    4441        null = self.sql(expression, "null")
    4442
    4443        return f"{empty}{error}{null}"

def jsonextractquote_sql(self, expression: sqlglot.expressions.JSONExtractQuote) -> str: View Source

    4445    def jsonextractquote_sql(self, expression: exp.JSONExtractQuote) -> str:
    4446        scalar = " ON SCALAR STRING" if expression.args.get("scalar") else ""
    4447        return f"{self.sql(expression, 'option')} QUOTES{scalar}"

def jsonexists_sql(self, expression: sqlglot.expressions.JSONExists) -> str: View Source

    4449    def jsonexists_sql(self, expression: exp.JSONExists) -> str:
    4450        this = self.sql(expression, "this")
    4451        path = self.sql(expression, "path")
    4452
    4453        passing = self.expressions(expression, "passing")
    4454        passing = f" PASSING {passing}" if passing else ""
    4455
    4456        on_condition = self.sql(expression, "on_condition")
    4457        on_condition = f" {on_condition}" if on_condition else ""
    4458
    4459        path = f"{path}{passing}{on_condition}"
    4460
    4461        return self.func("JSON_EXISTS", this, path)

def arrayagg_sql(self, expression: sqlglot.expressions.ArrayAgg) -> str: View Source

    4463    def arrayagg_sql(self, expression: exp.ArrayAgg) -> str:
    4464        array_agg = self.function_fallback_sql(expression)
    4465
    4466        # Add a NULL FILTER on the column to mimic the results going from a dialect that excludes nulls
    4467        # on ARRAY_AGG (e.g Spark) to one that doesn't (e.g. DuckDB)
    4468        if self.dialect.ARRAY_AGG_INCLUDES_NULLS and expression.args.get("nulls_excluded"):
    4469            parent = expression.parent
    4470            if isinstance(parent, exp.Filter):
    4471                parent_cond = parent.expression.this
    4472                parent_cond.replace(parent_cond.and_(expression.this.is_(exp.null()).not_()))
    4473            else:
    4474                this = expression.this
    4475                # Do not add the filter if the input is not a column (e.g. literal, struct etc)
    4476                if this.find(exp.Column):
    4477                    # DISTINCT is already present in the agg function, do not propagate it to FILTER as well
    4478                    this_sql = (
    4479                        self.expressions(this)
    4480                        if isinstance(this, exp.Distinct)
    4481                        else self.sql(expression, "this")
    4482                    )
    4483
    4484                    array_agg = f"{array_agg} FILTER(WHERE {this_sql} IS NOT NULL)"
    4485
    4486        return array_agg

def apply_sql(self, expression: sqlglot.expressions.Apply) -> str: View Source

    4488    def apply_sql(self, expression: exp.Apply) -> str:
    4489        this = self.sql(expression, "this")
    4490        expr = self.sql(expression, "expression")
    4491
    4492        return f"{this} APPLY({expr})"

def grant_sql(self, expression: sqlglot.expressions.Grant) -> str: View Source

    4494    def grant_sql(self, expression: exp.Grant) -> str:
    4495        privileges_sql = self.expressions(expression, key="privileges", flat=True)
    4496
    4497        kind = self.sql(expression, "kind")
    4498        kind = f" {kind}" if kind else ""
    4499
    4500        securable = self.sql(expression, "securable")
    4501        securable = f" {securable}" if securable else ""
    4502
    4503        principals = self.expressions(expression, key="principals", flat=True)
    4504
    4505        grant_option = " WITH GRANT OPTION" if expression.args.get("grant_option") else ""
    4506
    4507        return f"GRANT {privileges_sql} ON{kind}{securable} TO {principals}{grant_option}"

def grantprivilege_sql(self, expression: sqlglot.expressions.GrantPrivilege): View Source

    4509    def grantprivilege_sql(self, expression: exp.GrantPrivilege):
    4510        this = self.sql(expression, "this")
    4511        columns = self.expressions(expression, flat=True)
    4512        columns = f"({columns})" if columns else ""
    4513
    4514        return f"{this}{columns}"

def grantprincipal_sql(self, expression: sqlglot.expressions.GrantPrincipal): View Source

    4516    def grantprincipal_sql(self, expression: exp.GrantPrincipal):
    4517        this = self.sql(expression, "this")
    4518
    4519        kind = self.sql(expression, "kind")
    4520        kind = f"{kind} " if kind else ""
    4521
    4522        return f"{kind}{this}"

def columns_sql(self, expression: sqlglot.expressions.Columns): View Source

    4524    def columns_sql(self, expression: exp.Columns):
    4525        func = self.function_fallback_sql(expression)
    4526        if expression.args.get("unpack"):
    4527            func = f"*{func}"
    4528
    4529        return func

def overlay_sql(self, expression: sqlglot.expressions.Overlay): View Source

    4531    def overlay_sql(self, expression: exp.Overlay):
    4532        this = self.sql(expression, "this")
    4533        expr = self.sql(expression, "expression")
    4534        from_sql = self.sql(expression, "from")
    4535        for_sql = self.sql(expression, "for")
    4536        for_sql = f" FOR {for_sql}" if for_sql else ""
    4537
    4538        return f"OVERLAY({this} PLACING {expr} FROM {from_sql}{for_sql})"

@unsupported_args('format')
def todouble_sql(self, expression: sqlglot.expressions.ToDouble) -> str: View Source

    4540    @unsupported_args("format")
    4541    def todouble_sql(self, expression: exp.ToDouble) -> str:
    4542        return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))

def string_sql(self, expression: sqlglot.expressions.String) -> str: View Source

    4544    def string_sql(self, expression: exp.String) -> str:
    4545        this = expression.this
    4546        zone = expression.args.get("zone")
    4547
    4548        if zone:
    4549            # This is a BigQuery specific argument for STRING(<timestamp_expr>, <time_zone>)
    4550            # BigQuery stores timestamps internally as UTC, so ConvertTimezone is used with UTC
    4551            # set for source_tz to transpile the time conversion before the STRING cast
    4552            this = exp.ConvertTimezone(
    4553                source_tz=exp.Literal.string("UTC"), target_tz=zone, timestamp=this
    4554            )
    4555
    4556        return self.sql(exp.cast(this, exp.DataType.Type.VARCHAR))

def median_sql(self, expression: sqlglot.expressions.Median): View Source

    4558    def median_sql(self, expression: exp.Median):
    4559        if not self.SUPPORTS_MEDIAN:
    4560            return self.sql(
    4561                exp.PercentileCont(this=expression.this, expression=exp.Literal.number(0.5))
    4562            )
    4563
    4564        return self.function_fallback_sql(expression)

def overflowtruncatebehavior_sql(self, expression: sqlglot.expressions.OverflowTruncateBehavior) -> str: View Source

    4566    def overflowtruncatebehavior_sql(self, expression: exp.OverflowTruncateBehavior) -> str:
    4567        filler = self.sql(expression, "this")
    4568        filler = f" {filler}" if filler else ""
    4569        with_count = "WITH COUNT" if expression.args.get("with_count") else "WITHOUT COUNT"
    4570        return f"TRUNCATE{filler} {with_count}"

def unixseconds_sql(self, expression: sqlglot.expressions.UnixSeconds) -> str: View Source

    4572    def unixseconds_sql(self, expression: exp.UnixSeconds) -> str:
    4573        if self.SUPPORTS_UNIX_SECONDS:
    4574            return self.function_fallback_sql(expression)
    4575
    4576        start_ts = exp.cast(
    4577            exp.Literal.string("1970-01-01 00:00:00+00"), to=exp.DataType.Type.TIMESTAMPTZ
    4578        )
    4579
    4580        return self.sql(
    4581            exp.TimestampDiff(this=expression.this, expression=start_ts, unit=exp.var("SECONDS"))
    4582        )

def arraysize_sql(self, expression: sqlglot.expressions.ArraySize) -> str: View Source

    4584    def arraysize_sql(self, expression: exp.ArraySize) -> str:
    4585        dim = expression.expression
    4586
    4587        # For dialects that don't support the dimension arg, we can safely transpile it's default value (1st dimension)
    4588        if dim and self.ARRAY_SIZE_DIM_REQUIRED is None:
    4589            if not (dim.is_int and dim.name == "1"):
    4590                self.unsupported("Cannot transpile dimension argument for ARRAY_LENGTH")
    4591            dim = None
    4592
    4593        # If dimension is required but not specified, default initialize it
    4594        if self.ARRAY_SIZE_DIM_REQUIRED and not dim:
    4595            dim = exp.Literal.number(1)
    4596
    4597        return self.func(self.ARRAY_SIZE_NAME, expression.this, dim)

def attach_sql(self, expression: sqlglot.expressions.Attach) -> str: View Source

    4599    def attach_sql(self, expression: exp.Attach) -> str:
    4600        this = self.sql(expression, "this")
    4601        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    4602        expressions = self.expressions(expression)
    4603        expressions = f" ({expressions})" if expressions else ""
    4604
    4605        return f"ATTACH{exists_sql} {this}{expressions}"

def detach_sql(self, expression: sqlglot.expressions.Detach) -> str: View Source

    4607    def detach_sql(self, expression: exp.Detach) -> str:
    4608        this = self.sql(expression, "this")
    4609        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
    4610
    4611        return f"DETACH{exists_sql} {this}"

def attachoption_sql(self, expression: sqlglot.expressions.AttachOption) -> str: View Source

    4613    def attachoption_sql(self, expression: exp.AttachOption) -> str:
    4614        this = self.sql(expression, "this")
    4615        value = self.sql(expression, "expression")
    4616        value = f" {value}" if value else ""
    4617        return f"{this}{value}"

def featuresattime_sql(self, expression: sqlglot.expressions.FeaturesAtTime) -> str: View Source

    4619    def featuresattime_sql(self, expression: exp.FeaturesAtTime) -> str:
    4620        this_sql = self.sql(expression, "this")
    4621        if isinstance(expression.this, exp.Table):
    4622            this_sql = f"TABLE {this_sql}"
    4623
    4624        return self.func(
    4625            "FEATURES_AT_TIME",
    4626            this_sql,
    4627            expression.args.get("time"),
    4628            expression.args.get("num_rows"),
    4629            expression.args.get("ignore_feature_nulls"),
    4630        )

def watermarkcolumnconstraint_sql(self, expression: sqlglot.expressions.WatermarkColumnConstraint) -> str: View Source

    4632    def watermarkcolumnconstraint_sql(self, expression: exp.WatermarkColumnConstraint) -> str:
    4633        return (
    4634            f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
    4635        )

def encodeproperty_sql(self, expression: sqlglot.expressions.EncodeProperty) -> str: View Source

    4637    def encodeproperty_sql(self, expression: exp.EncodeProperty) -> str:
    4638        encode = "KEY ENCODE" if expression.args.get("key") else "ENCODE"
    4639        encode = f"{encode} {self.sql(expression, 'this')}"
    4640
    4641        properties = expression.args.get("properties")
    4642        if properties:
    4643            encode = f"{encode} {self.properties(properties)}"
    4644
    4645        return encode

def includeproperty_sql(self, expression: sqlglot.expressions.IncludeProperty) -> str: View Source

    4647    def includeproperty_sql(self, expression: exp.IncludeProperty) -> str:
    4648        this = self.sql(expression, "this")
    4649        include = f"INCLUDE {this}"
    4650
    4651        column_def = self.sql(expression, "column_def")
    4652        if column_def:
    4653            include = f"{include} {column_def}"
    4654
    4655        alias = self.sql(expression, "alias")
    4656        if alias:
    4657            include = f"{include} AS {alias}"
    4658
    4659        return include

def xmlelement_sql(self, expression: sqlglot.expressions.XMLElement) -> str: View Source

    4661    def xmlelement_sql(self, expression: exp.XMLElement) -> str:
    4662        name = f"NAME {self.sql(expression, 'this')}"
    4663        return self.func("XMLELEMENT", name, *expression.expressions)

def partitionbyrangeproperty_sql(self, expression: sqlglot.expressions.PartitionByRangeProperty) -> str: View Source

    4665    def partitionbyrangeproperty_sql(self, expression: exp.PartitionByRangeProperty) -> str:
    4666        partitions = self.expressions(expression, "partition_expressions")
    4667        create = self.expressions(expression, "create_expressions")
    4668        return f"PARTITION BY RANGE {self.wrap(partitions)} {self.wrap(create)}"

def partitionbyrangepropertydynamic_sql( self, expression: sqlglot.expressions.PartitionByRangePropertyDynamic) -> str: View Source

    4670    def partitionbyrangepropertydynamic_sql(
    4671        self, expression: exp.PartitionByRangePropertyDynamic
    4672    ) -> str:
    4673        start = self.sql(expression, "start")
    4674        end = self.sql(expression, "end")
    4675
    4676        every = expression.args["every"]
    4677        if isinstance(every, exp.Interval) and every.this.is_string:
    4678            every.this.replace(exp.Literal.number(every.name))
    4679
    4680        return f"START {self.wrap(start)} END {self.wrap(end)} EVERY {self.wrap(self.sql(every))}"

def unpivotcolumns_sql(self, expression: sqlglot.expressions.UnpivotColumns) -> str: View Source

    4682    def unpivotcolumns_sql(self, expression: exp.UnpivotColumns) -> str:
    4683        name = self.sql(expression, "this")
    4684        values = self.expressions(expression, flat=True)
    4685
    4686        return f"NAME {name} VALUE {values}"

def analyzesample_sql(self, expression: sqlglot.expressions.AnalyzeSample) -> str: View Source

    4688    def analyzesample_sql(self, expression: exp.AnalyzeSample) -> str:
    4689        kind = self.sql(expression, "kind")
    4690        sample = self.sql(expression, "sample")
    4691        return f"SAMPLE {sample} {kind}"

def analyzestatistics_sql(self, expression: sqlglot.expressions.AnalyzeStatistics) -> str: View Source

    4693    def analyzestatistics_sql(self, expression: exp.AnalyzeStatistics) -> str:
    4694        kind = self.sql(expression, "kind")
    4695        option = self.sql(expression, "option")
    4696        option = f" {option}" if option else ""
    4697        this = self.sql(expression, "this")
    4698        this = f" {this}" if this else ""
    4699        columns = self.expressions(expression)
    4700        columns = f" {columns}" if columns else ""
    4701        return f"{kind}{option} STATISTICS{this}{columns}"

def analyzehistogram_sql(self, expression: sqlglot.expressions.AnalyzeHistogram) -> str: View Source

    4703    def analyzehistogram_sql(self, expression: exp.AnalyzeHistogram) -> str:
    4704        this = self.sql(expression, "this")
    4705        columns = self.expressions(expression)
    4706        inner_expression = self.sql(expression, "expression")
    4707        inner_expression = f" {inner_expression}" if inner_expression else ""
    4708        update_options = self.sql(expression, "update_options")
    4709        update_options = f" {update_options} UPDATE" if update_options else ""
    4710        return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"

def analyzedelete_sql(self, expression: sqlglot.expressions.AnalyzeDelete) -> str: View Source

    4712    def analyzedelete_sql(self, expression: exp.AnalyzeDelete) -> str:
    4713        kind = self.sql(expression, "kind")
    4714        kind = f" {kind}" if kind else ""
    4715        return f"DELETE{kind} STATISTICS"

def analyzelistchainedrows_sql(self, expression: sqlglot.expressions.AnalyzeListChainedRows) -> str: View Source

    4717    def analyzelistchainedrows_sql(self, expression: exp.AnalyzeListChainedRows) -> str:
    4718        inner_expression = self.sql(expression, "expression")
    4719        return f"LIST CHAINED ROWS{inner_expression}"

def analyzevalidate_sql(self, expression: sqlglot.expressions.AnalyzeValidate) -> str: View Source

    4721    def analyzevalidate_sql(self, expression: exp.AnalyzeValidate) -> str:
    4722        kind = self.sql(expression, "kind")
    4723        this = self.sql(expression, "this")
    4724        this = f" {this}" if this else ""
    4725        inner_expression = self.sql(expression, "expression")
    4726        return f"VALIDATE {kind}{this}{inner_expression}"

def analyze_sql(self, expression: sqlglot.expressions.Analyze) -> str: View Source

    4728    def analyze_sql(self, expression: exp.Analyze) -> str:
    4729        options = self.expressions(expression, key="options", sep=" ")
    4730        options = f" {options}" if options else ""
    4731        kind = self.sql(expression, "kind")
    4732        kind = f" {kind}" if kind else ""
    4733        this = self.sql(expression, "this")
    4734        this = f" {this}" if this else ""
    4735        mode = self.sql(expression, "mode")
    4736        mode = f" {mode}" if mode else ""
    4737        properties = self.sql(expression, "properties")
    4738        properties = f" {properties}" if properties else ""
    4739        partition = self.sql(expression, "partition")
    4740        partition = f" {partition}" if partition else ""
    4741        inner_expression = self.sql(expression, "expression")
    4742        inner_expression = f" {inner_expression}" if inner_expression else ""
    4743        return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"

def xmltable_sql(self, expression: sqlglot.expressions.XMLTable) -> str: View Source

    4745    def xmltable_sql(self, expression: exp.XMLTable) -> str:
    4746        this = self.sql(expression, "this")
    4747        namespaces = self.expressions(expression, key="namespaces")
    4748        namespaces = f"XMLNAMESPACES({namespaces}), " if namespaces else ""
    4749        passing = self.expressions(expression, key="passing")
    4750        passing = f"{self.sep()}PASSING{self.seg(passing)}" if passing else ""
    4751        columns = self.expressions(expression, key="columns")
    4752        columns = f"{self.sep()}COLUMNS{self.seg(columns)}" if columns else ""
    4753        by_ref = f"{self.sep()}RETURNING SEQUENCE BY REF" if expression.args.get("by_ref") else ""
    4754        return f"XMLTABLE({self.sep('')}{self.indent(namespaces + this + passing + by_ref + columns)}{self.seg(')', sep='')}"

def xmlnamespace_sql(self, expression: sqlglot.expressions.XMLNamespace) -> str: View Source

    4756    def xmlnamespace_sql(self, expression: exp.XMLNamespace) -> str:
    4757        this = self.sql(expression, "this")
    4758        return this if isinstance(expression.this, exp.Alias) else f"DEFAULT {this}"

def export_sql(self, expression: sqlglot.expressions.Export) -> str: View Source

    4760    def export_sql(self, expression: exp.Export) -> str:
    4761        this = self.sql(expression, "this")
    4762        connection = self.sql(expression, "connection")
    4763        connection = f"WITH CONNECTION {connection} " if connection else ""
    4764        options = self.sql(expression, "options")
    4765        return f"EXPORT DATA {connection}{options} AS {this}"