Edit on GitHub

sqlglot.generator

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

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. Default: 2.
  • indent: The indentation size in a formatted string. 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.Dialect, Type[sqlglot.dialects.dialect.Dialect], NoneType] = None)
487    def __init__(
488        self,
489        pretty: t.Optional[bool] = None,
490        identify: str | bool = False,
491        normalize: bool = False,
492        pad: int = 2,
493        indent: int = 2,
494        normalize_functions: t.Optional[str | bool] = None,
495        unsupported_level: ErrorLevel = ErrorLevel.WARN,
496        max_unsupported: int = 3,
497        leading_comma: bool = False,
498        max_text_width: int = 80,
499        comments: bool = True,
500        dialect: DialectType = None,
501    ):
502        import sqlglot
503        from sqlglot.dialects import Dialect
504
505        self.pretty = pretty if pretty is not None else sqlglot.pretty
506        self.identify = identify
507        self.normalize = normalize
508        self.pad = pad
509        self._indent = indent
510        self.unsupported_level = unsupported_level
511        self.max_unsupported = max_unsupported
512        self.leading_comma = leading_comma
513        self.max_text_width = max_text_width
514        self.comments = comments
515        self.dialect = Dialect.get_or_raise(dialect)
516
517        # This is both a Dialect property and a Generator argument, so we prioritize the latter
518        self.normalize_functions = (
519            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
520        )
521
522        self.unsupported_messages: t.List[str] = []
523        self._escaped_quote_end: str = (
524            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
525        )
526        self._escaped_identifier_end: str = (
527            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
528        )
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.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateAdd'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <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.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <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.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <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.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXPLICIT_UNION = False
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
COLUMN_JOIN_MARKS_SUPPORTED = False
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
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
CAN_IMPLEMENT_ARRAY_ANY = False
TYPE_MAPPING = {<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET'}
STAR_MAPPING = {'except': 'EXCEPT', 'replace': 'REPLACE'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
PROPERTIES_LOCATION = {<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.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <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.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.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.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
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:
530    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
531        """
532        Generates the SQL string corresponding to the given syntax tree.
533
534        Args:
535            expression: The syntax tree.
536            copy: Whether to copy the expression. The generator performs mutations so
537                it is safer to copy.
538
539        Returns:
540            The SQL string corresponding to `expression`.
541        """
542        if copy:
543            expression = expression.copy()
544
545        expression = self.preprocess(expression)
546
547        self.unsupported_messages = []
548        sql = self.sql(expression).strip()
549
550        if self.pretty:
551            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
552
553        if self.unsupported_level == ErrorLevel.IGNORE:
554            return sql
555
556        if self.unsupported_level == ErrorLevel.WARN:
557            for msg in self.unsupported_messages:
558                logger.warning(msg)
559        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
560            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
561
562        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:
564    def preprocess(self, expression: exp.Expression) -> exp.Expression:
565        """Apply generic preprocessing transformations to a given expression."""
566        if (
567            not expression.parent
568            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
569            and any(node.parent is not expression for node in expression.find_all(exp.With))
570        ):
571            from sqlglot.transforms import move_ctes_to_top_level
572
573            expression = move_ctes_to_top_level(expression)
574
575        if self.ENSURE_BOOLS:
576            from sqlglot.transforms import ensure_bools
577
578            expression = ensure_bools(expression)
579
580        return expression

Apply generic preprocessing transformations to a given expression.

def unsupported(self, message: str) -> None:
582    def unsupported(self, message: str) -> None:
583        if self.unsupported_level == ErrorLevel.IMMEDIATE:
584            raise UnsupportedError(message)
585        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
587    def sep(self, sep: str = " ") -> str:
588        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
590    def seg(self, sql: str, sep: str = " ") -> str:
591        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
593    def pad_comment(self, comment: str) -> str:
594        comment = " " + comment if comment[0].strip() else comment
595        comment = comment + " " if comment[-1].strip() else comment
596        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None) -> str:
598    def maybe_comment(
599        self,
600        sql: str,
601        expression: t.Optional[exp.Expression] = None,
602        comments: t.Optional[t.List[str]] = None,
603    ) -> str:
604        comments = (
605            ((expression and expression.comments) if comments is None else comments)  # type: ignore
606            if self.comments
607            else None
608        )
609
610        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
611            return sql
612
613        comments_sql = " ".join(
614            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
615        )
616
617        if not comments_sql:
618            return sql
619
620        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
621            return (
622                f"{self.sep()}{comments_sql}{sql}"
623                if sql[0].isspace()
624                else f"{comments_sql}{self.sep()}{sql}"
625            )
626
627        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
629    def wrap(self, expression: exp.Expression | str) -> str:
630        this_sql = self.indent(
631            (
632                self.sql(expression)
633                if isinstance(expression, exp.UNWRAPPED_QUERIES)
634                else self.sql(expression, "this")
635            ),
636            level=1,
637            pad=0,
638        )
639        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
641    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
642        original = self.identify
643        self.identify = False
644        result = func(*args, **kwargs)
645        self.identify = original
646        return result
def normalize_func(self, name: str) -> str:
648    def normalize_func(self, name: str) -> str:
649        if self.normalize_functions == "upper" or self.normalize_functions is True:
650            return name.upper()
651        if self.normalize_functions == "lower":
652            return name.lower()
653        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
655    def indent(
656        self,
657        sql: str,
658        level: int = 0,
659        pad: t.Optional[int] = None,
660        skip_first: bool = False,
661        skip_last: bool = False,
662    ) -> str:
663        if not self.pretty:
664            return sql
665
666        pad = self.pad if pad is None else pad
667        lines = sql.split("\n")
668
669        return "\n".join(
670            (
671                line
672                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
673                else f"{' ' * (level * self._indent + pad)}{line}"
674            )
675            for i, line in enumerate(lines)
676        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
678    def sql(
679        self,
680        expression: t.Optional[str | exp.Expression],
681        key: t.Optional[str] = None,
682        comment: bool = True,
683    ) -> str:
684        if not expression:
685            return ""
686
687        if isinstance(expression, str):
688            return expression
689
690        if key:
691            value = expression.args.get(key)
692            if value:
693                return self.sql(value)
694            return ""
695
696        transform = self.TRANSFORMS.get(expression.__class__)
697
698        if callable(transform):
699            sql = transform(self, expression)
700        elif isinstance(expression, exp.Expression):
701            exp_handler_name = f"{expression.key}_sql"
702
703            if hasattr(self, exp_handler_name):
704                sql = getattr(self, exp_handler_name)(expression)
705            elif isinstance(expression, exp.Func):
706                sql = self.function_fallback_sql(expression)
707            elif isinstance(expression, exp.Property):
708                sql = self.property_sql(expression)
709            else:
710                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
711        else:
712            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
713
714        return self.maybe_comment(sql, expression) if self.comments and comment else sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
716    def uncache_sql(self, expression: exp.Uncache) -> str:
717        table = self.sql(expression, "this")
718        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
719        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
721    def cache_sql(self, expression: exp.Cache) -> str:
722        lazy = " LAZY" if expression.args.get("lazy") else ""
723        table = self.sql(expression, "this")
724        options = expression.args.get("options")
725        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
726        sql = self.sql(expression, "expression")
727        sql = f" AS{self.sep()}{sql}" if sql else ""
728        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
729        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
731    def characterset_sql(self, expression: exp.CharacterSet) -> str:
732        if isinstance(expression.parent, exp.Cast):
733            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
734        default = "DEFAULT " if expression.args.get("default") else ""
735        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
737    def column_sql(self, expression: exp.Column) -> str:
738        join_mark = " (+)" if expression.args.get("join_mark") else ""
739
740        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
741            join_mark = ""
742            self.unsupported("Outer join syntax using the (+) operator is not supported.")
743
744        column = ".".join(
745            self.sql(part)
746            for part in (
747                expression.args.get("catalog"),
748                expression.args.get("db"),
749                expression.args.get("table"),
750                expression.args.get("this"),
751            )
752            if part
753        )
754
755        return f"{column}{join_mark}"
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
757    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
758        this = self.sql(expression, "this")
759        this = f" {this}" if this else ""
760        position = self.sql(expression, "position")
761        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
763    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
764        column = self.sql(expression, "this")
765        kind = self.sql(expression, "kind")
766        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
767        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
768        kind = f"{sep}{kind}" if kind else ""
769        constraints = f" {constraints}" if constraints else ""
770        position = self.sql(expression, "position")
771        position = f" {position}" if position else ""
772
773        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
774            kind = ""
775
776        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
778    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
779        this = self.sql(expression, "this")
780        kind_sql = self.sql(expression, "kind").strip()
781        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
783    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
784        this = self.sql(expression, "this")
785        if expression.args.get("not_null"):
786            persisted = " PERSISTED NOT NULL"
787        elif expression.args.get("persisted"):
788            persisted = " PERSISTED"
789        else:
790            persisted = ""
791        return f"AS {this}{persisted}"
def autoincrementcolumnconstraint_sql(self, _) -> str:
793    def autoincrementcolumnconstraint_sql(self, _) -> str:
794        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
796    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
797        if isinstance(expression.this, list):
798            this = self.wrap(self.expressions(expression, key="this", flat=True))
799        else:
800            this = self.sql(expression, "this")
801
802        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
804    def generatedasidentitycolumnconstraint_sql(
805        self, expression: exp.GeneratedAsIdentityColumnConstraint
806    ) -> str:
807        this = ""
808        if expression.this is not None:
809            on_null = " ON NULL" if expression.args.get("on_null") else ""
810            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
811
812        start = expression.args.get("start")
813        start = f"START WITH {start}" if start else ""
814        increment = expression.args.get("increment")
815        increment = f" INCREMENT BY {increment}" if increment else ""
816        minvalue = expression.args.get("minvalue")
817        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
818        maxvalue = expression.args.get("maxvalue")
819        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
820        cycle = expression.args.get("cycle")
821        cycle_sql = ""
822
823        if cycle is not None:
824            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
825            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
826
827        sequence_opts = ""
828        if start or increment or cycle_sql:
829            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
830            sequence_opts = f" ({sequence_opts.strip()})"
831
832        expr = self.sql(expression, "expression")
833        expr = f"({expr})" if expr else "IDENTITY"
834
835        return f"GENERATED{this} AS {expr}{sequence_opts}"
def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str:
837    def generatedasrowcolumnconstraint_sql(
838        self, expression: exp.GeneratedAsRowColumnConstraint
839    ) -> str:
840        start = "START" if expression.args.get("start") else "END"
841        hidden = " HIDDEN" if expression.args.get("hidden") else ""
842        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str:
844    def periodforsystemtimeconstraint_sql(
845        self, expression: exp.PeriodForSystemTimeConstraint
846    ) -> str:
847        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
849    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
850        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str:
852    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
853        return f"AS {self.sql(expression, 'this')}"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
855    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
856        desc = expression.args.get("desc")
857        if desc is not None:
858            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
859        return "PRIMARY KEY"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
861    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
862        this = self.sql(expression, "this")
863        this = f" {this}" if this else ""
864        index_type = expression.args.get("index_type")
865        index_type = f" USING {index_type}" if index_type else ""
866        return f"UNIQUE{this}{index_type}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str:
868    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
869        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
871    def create_sql(self, expression: exp.Create) -> str:
872        kind = self.sql(expression, "kind")
873        properties = expression.args.get("properties")
874        properties_locs = self.locate_properties(properties) if properties else defaultdict()
875
876        this = self.createable_sql(expression, properties_locs)
877
878        properties_sql = ""
879        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
880            exp.Properties.Location.POST_WITH
881        ):
882            properties_sql = self.sql(
883                exp.Properties(
884                    expressions=[
885                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
886                        *properties_locs[exp.Properties.Location.POST_WITH],
887                    ]
888                )
889            )
890
891        begin = " BEGIN" if expression.args.get("begin") else ""
892        end = " END" if expression.args.get("end") else ""
893
894        expression_sql = self.sql(expression, "expression")
895        if expression_sql:
896            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
897
898            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
899                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
900                    postalias_props_sql = self.properties(
901                        exp.Properties(
902                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
903                        ),
904                        wrapped=False,
905                    )
906                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
907                else:
908                    expression_sql = f" AS{expression_sql}"
909
910        postindex_props_sql = ""
911        if properties_locs.get(exp.Properties.Location.POST_INDEX):
912            postindex_props_sql = self.properties(
913                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
914                wrapped=False,
915                prefix=" ",
916            )
917
918        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
919        indexes = f" {indexes}" if indexes else ""
920        index_sql = indexes + postindex_props_sql
921
922        replace = " OR REPLACE" if expression.args.get("replace") else ""
923        unique = " UNIQUE" if expression.args.get("unique") else ""
924
925        postcreate_props_sql = ""
926        if properties_locs.get(exp.Properties.Location.POST_CREATE):
927            postcreate_props_sql = self.properties(
928                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
929                sep=" ",
930                prefix=" ",
931                wrapped=False,
932            )
933
934        modifiers = "".join((replace, unique, postcreate_props_sql))
935
936        postexpression_props_sql = ""
937        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
938            postexpression_props_sql = self.properties(
939                exp.Properties(
940                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
941                ),
942                sep=" ",
943                prefix=" ",
944                wrapped=False,
945            )
946
947        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
948        no_schema_binding = (
949            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
950        )
951
952        clone = self.sql(expression, "clone")
953        clone = f" {clone}" if clone else ""
954
955        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
956        return self.prepend_ctes(expression, expression_sql)
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
958    def clone_sql(self, expression: exp.Clone) -> str:
959        this = self.sql(expression, "this")
960        shallow = "SHALLOW " if expression.args.get("shallow") else ""
961        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
962        return f"{shallow}{keyword} {this}"
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
964    def describe_sql(self, expression: exp.Describe) -> str:
965        extended = " EXTENDED" if expression.args.get("extended") else ""
966        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str:
968    def heredoc_sql(self, expression: exp.Heredoc) -> str:
969        tag = self.sql(expression, "tag")
970        return f"${tag}${self.sql(expression, 'this')}${tag}$"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
972    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
973        with_ = self.sql(expression, "with")
974        if with_:
975            sql = f"{with_}{self.sep()}{sql}"
976        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
978    def with_sql(self, expression: exp.With) -> str:
979        sql = self.expressions(expression, flat=True)
980        recursive = (
981            "RECURSIVE "
982            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
983            else ""
984        )
985
986        return f"WITH {recursive}{sql}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
988    def cte_sql(self, expression: exp.CTE) -> str:
989        alias = self.sql(expression, "alias")
990        return f"{alias} AS {self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
 992    def tablealias_sql(self, expression: exp.TableAlias) -> str:
 993        alias = self.sql(expression, "this")
 994        columns = self.expressions(expression, key="columns", flat=True)
 995        columns = f"({columns})" if columns else ""
 996
 997        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
 998            columns = ""
 999            self.unsupported("Named columns are not supported in table alias.")
1000
1001        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1002            alias = "_t"
1003
1004        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
1006    def bitstring_sql(self, expression: exp.BitString) -> str:
1007        this = self.sql(expression, "this")
1008        if self.dialect.BIT_START:
1009            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1010        return f"{int(this, 2)}"
def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str:
1012    def hexstring_sql(self, expression: exp.HexString) -> str:
1013        this = self.sql(expression, "this")
1014        if self.dialect.HEX_START:
1015            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1016        return f"{int(this, 16)}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
1018    def bytestring_sql(self, expression: exp.ByteString) -> str:
1019        this = self.sql(expression, "this")
1020        if self.dialect.BYTE_START:
1021            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1022        return this
def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str:
1024    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1025        this = self.sql(expression, "this")
1026        escape = expression.args.get("escape")
1027
1028        if self.dialect.UNICODE_START:
1029            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
1030            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
1031
1032        if escape:
1033            pattern = re.compile(rf"{escape.name}(\d+)")
1034        else:
1035            pattern = ESCAPED_UNICODE_RE
1036
1037        this = pattern.sub(r"\\u\1", this)
1038        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
1040    def rawstring_sql(self, expression: exp.RawString) -> str:
1041        string = self.escape_str(expression.this.replace("\\", "\\\\"))
1042        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str:
1044    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1045        this = self.sql(expression, "this")
1046        specifier = self.sql(expression, "expression")
1047        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1048        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
1050    def datatype_sql(self, expression: exp.DataType) -> str:
1051        type_value = expression.this
1052
1053        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1054            type_sql = self.sql(expression, "kind")
1055        else:
1056            type_sql = (
1057                self.TYPE_MAPPING.get(type_value, type_value.value)
1058                if isinstance(type_value, exp.DataType.Type)
1059                else type_value
1060            )
1061
1062        nested = ""
1063        interior = self.expressions(expression, flat=True)
1064        values = ""
1065
1066        if interior:
1067            if expression.args.get("nested"):
1068                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1069                if expression.args.get("values") is not None:
1070                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1071                    values = self.expressions(expression, key="values", flat=True)
1072                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1073            elif type_value == exp.DataType.Type.INTERVAL:
1074                nested = f" {interior}"
1075            else:
1076                nested = f"({interior})"
1077
1078        type_sql = f"{type_sql}{nested}{values}"
1079        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1080            exp.DataType.Type.TIMETZ,
1081            exp.DataType.Type.TIMESTAMPTZ,
1082        ):
1083            type_sql = f"{type_sql} WITH TIME ZONE"
1084
1085        return type_sql
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
1087    def directory_sql(self, expression: exp.Directory) -> str:
1088        local = "LOCAL " if expression.args.get("local") else ""
1089        row_format = self.sql(expression, "row_format")
1090        row_format = f" {row_format}" if row_format else ""
1091        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
1093    def delete_sql(self, expression: exp.Delete) -> str:
1094        this = self.sql(expression, "this")
1095        this = f" FROM {this}" if this else ""
1096        using = self.sql(expression, "using")
1097        using = f" USING {using}" if using else ""
1098        where = self.sql(expression, "where")
1099        returning = self.sql(expression, "returning")
1100        limit = self.sql(expression, "limit")
1101        tables = self.expressions(expression, key="tables")
1102        tables = f" {tables}" if tables else ""
1103        if self.RETURNING_END:
1104            expression_sql = f"{this}{using}{where}{returning}{limit}"
1105        else:
1106            expression_sql = f"{returning}{this}{using}{where}{limit}"
1107        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
1109    def drop_sql(self, expression: exp.Drop) -> str:
1110        this = self.sql(expression, "this")
1111        kind = expression.args["kind"]
1112        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1113        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1114        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1115        cascade = " CASCADE" if expression.args.get("cascade") else ""
1116        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1117        purge = " PURGE" if expression.args.get("purge") else ""
1118        return (
1119            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
1120        )
def except_sql(self, expression: sqlglot.expressions.Except) -> str:
1122    def except_sql(self, expression: exp.Except) -> str:
1123        return self.prepend_ctes(
1124            expression,
1125            self.set_operation(expression, self.except_op(expression)),
1126        )
def except_op(self, expression: sqlglot.expressions.Except) -> str:
1128    def except_op(self, expression: exp.Except) -> str:
1129        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
1131    def fetch_sql(self, expression: exp.Fetch) -> str:
1132        direction = expression.args.get("direction")
1133        direction = f" {direction}" if direction else ""
1134        count = expression.args.get("count")
1135        count = f" {count}" if count else ""
1136        if expression.args.get("percent"):
1137            count = f"{count} PERCENT"
1138        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
1139        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
1141    def filter_sql(self, expression: exp.Filter) -> str:
1142        if self.AGGREGATE_FILTER_SUPPORTED:
1143            this = self.sql(expression, "this")
1144            where = self.sql(expression, "expression").strip()
1145            return f"{this} FILTER({where})"
1146
1147        agg = expression.this
1148        agg_arg = agg.this
1149        cond = expression.expression.this
1150        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1151        return self.sql(agg)
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
1153    def hint_sql(self, expression: exp.Hint) -> str:
1154        if not self.QUERY_HINTS:
1155            self.unsupported("Hints are not supported")
1156            return ""
1157
1158        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
1160    def index_sql(self, expression: exp.Index) -> str:
1161        unique = "UNIQUE " if expression.args.get("unique") else ""
1162        primary = "PRIMARY " if expression.args.get("primary") else ""
1163        amp = "AMP " if expression.args.get("amp") else ""
1164        name = self.sql(expression, "this")
1165        name = f"{name} " if name else ""
1166        table = self.sql(expression, "table")
1167        table = f"{self.INDEX_ON} {table}" if table else ""
1168        using = self.sql(expression, "using")
1169        using = f" USING {using}" if using else ""
1170        index = "INDEX " if not table else ""
1171        columns = self.expressions(expression, key="columns", flat=True)
1172        columns = f"({columns})" if columns else ""
1173        partition_by = self.expressions(expression, key="partition_by", flat=True)
1174        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1175        where = self.sql(expression, "where")
1176        include = self.expressions(expression, key="include", flat=True)
1177        if include:
1178            include = f" INCLUDE ({include})"
1179        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
1181    def identifier_sql(self, expression: exp.Identifier) -> str:
1182        text = expression.name
1183        lower = text.lower()
1184        text = lower if self.normalize and not expression.quoted else text
1185        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
1186        if (
1187            expression.quoted
1188            or self.dialect.can_identify(text, self.identify)
1189            or lower in self.RESERVED_KEYWORDS
1190            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1191        ):
1192            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
1193        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
1195    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1196        input_format = self.sql(expression, "input_format")
1197        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1198        output_format = self.sql(expression, "output_format")
1199        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1200        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
1202    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1203        string = self.sql(exp.Literal.string(expression.name))
1204        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
1206    def partition_sql(self, expression: exp.Partition) -> str:
1207        return f"PARTITION({self.expressions(expression, flat=True)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
1209    def properties_sql(self, expression: exp.Properties) -> str:
1210        root_properties = []
1211        with_properties = []
1212
1213        for p in expression.expressions:
1214            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1215            if p_loc == exp.Properties.Location.POST_WITH:
1216                with_properties.append(p)
1217            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1218                root_properties.append(p)
1219
1220        return self.root_properties(
1221            exp.Properties(expressions=root_properties)
1222        ) + self.with_properties(exp.Properties(expressions=with_properties))
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
1224    def root_properties(self, properties: exp.Properties) -> str:
1225        if properties.expressions:
1226            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1227        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1229    def properties(
1230        self,
1231        properties: exp.Properties,
1232        prefix: str = "",
1233        sep: str = ", ",
1234        suffix: str = "",
1235        wrapped: bool = True,
1236    ) -> str:
1237        if properties.expressions:
1238            expressions = self.expressions(properties, sep=sep, indent=False)
1239            if expressions:
1240                expressions = self.wrap(expressions) if wrapped else expressions
1241                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1242        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
1244    def with_properties(self, properties: exp.Properties) -> str:
1245        return self.properties(properties, prefix=self.seg("WITH"))
def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict:
1247    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1248        properties_locs = defaultdict(list)
1249        for p in properties.expressions:
1250            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1251            if p_loc != exp.Properties.Location.UNSUPPORTED:
1252                properties_locs[p_loc].append(p)
1253            else:
1254                self.unsupported(f"Unsupported property {p.key}")
1255
1256        return properties_locs
def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1258    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1259        if isinstance(expression.this, exp.Dot):
1260            return self.sql(expression, "this")
1261        return f"'{expression.name}'" if string_key else expression.name
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
1263    def property_sql(self, expression: exp.Property) -> str:
1264        property_cls = expression.__class__
1265        if property_cls == exp.Property:
1266            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1267
1268        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1269        if not property_name:
1270            self.unsupported(f"Unsupported property {expression.key}")
1271
1272        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
1274    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1275        if self.SUPPORTS_CREATE_TABLE_LIKE:
1276            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1277            options = f" {options}" if options else ""
1278
1279            like = f"LIKE {self.sql(expression, 'this')}{options}"
1280            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1281                like = f"({like})"
1282
1283            return like
1284
1285        if expression.expressions:
1286            self.unsupported("Transpilation of LIKE property options is unsupported")
1287
1288        select = exp.select("*").from_(expression.this).limit(0)
1289        return f"AS {self.sql(select)}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
1291    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1292        no = "NO " if expression.args.get("no") else ""
1293        protection = " PROTECTION" if expression.args.get("protection") else ""
1294        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
1296    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1297        no = "NO " if expression.args.get("no") else ""
1298        local = expression.args.get("local")
1299        local = f"{local} " if local else ""
1300        dual = "DUAL " if expression.args.get("dual") else ""
1301        before = "BEFORE " if expression.args.get("before") else ""
1302        after = "AFTER " if expression.args.get("after") else ""
1303        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1305    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1306        freespace = self.sql(expression, "this")
1307        percent = " PERCENT" if expression.args.get("percent") else ""
1308        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1310    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1311        if expression.args.get("default"):
1312            property = "DEFAULT"
1313        elif expression.args.get("on"):
1314            property = "ON"
1315        else:
1316            property = "OFF"
1317        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1319    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1320        if expression.args.get("no"):
1321            return "NO MERGEBLOCKRATIO"
1322        if expression.args.get("default"):
1323            return "DEFAULT MERGEBLOCKRATIO"
1324
1325        percent = " PERCENT" if expression.args.get("percent") else ""
1326        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1328    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1329        default = expression.args.get("default")
1330        minimum = expression.args.get("minimum")
1331        maximum = expression.args.get("maximum")
1332        if default or minimum or maximum:
1333            if default:
1334                prop = "DEFAULT"
1335            elif minimum:
1336                prop = "MINIMUM"
1337            else:
1338                prop = "MAXIMUM"
1339            return f"{prop} DATABLOCKSIZE"
1340        units = expression.args.get("units")
1341        units = f" {units}" if units else ""
1342        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1344    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1345        autotemp = expression.args.get("autotemp")
1346        always = expression.args.get("always")
1347        default = expression.args.get("default")
1348        manual = expression.args.get("manual")
1349        never = expression.args.get("never")
1350
1351        if autotemp is not None:
1352            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1353        elif always:
1354            prop = "ALWAYS"
1355        elif default:
1356            prop = "DEFAULT"
1357        elif manual:
1358            prop = "MANUAL"
1359        elif never:
1360            prop = "NEVER"
1361        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1363    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1364        no = expression.args.get("no")
1365        no = " NO" if no else ""
1366        concurrent = expression.args.get("concurrent")
1367        concurrent = " CONCURRENT" if concurrent else ""
1368
1369        for_ = ""
1370        if expression.args.get("for_all"):
1371            for_ = " FOR ALL"
1372        elif expression.args.get("for_insert"):
1373            for_ = " FOR INSERT"
1374        elif expression.args.get("for_none"):
1375            for_ = " FOR NONE"
1376        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str:
1378    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1379        if isinstance(expression.this, list):
1380            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1381        if expression.this:
1382            modulus = self.sql(expression, "this")
1383            remainder = self.sql(expression, "expression")
1384            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1385
1386        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1387        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1388        return f"FROM ({from_expressions}) TO ({to_expressions})"
def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str:
1390    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1391        this = self.sql(expression, "this")
1392
1393        for_values_or_default = expression.expression
1394        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1395            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1396        else:
1397            for_values_or_default = " DEFAULT"
1398
1399        return f"PARTITION OF {this}{for_values_or_default}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1401    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1402        kind = expression.args.get("kind")
1403        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1404        for_or_in = expression.args.get("for_or_in")
1405        for_or_in = f" {for_or_in}" if for_or_in else ""
1406        lock_type = expression.args.get("lock_type")
1407        override = " OVERRIDE" if expression.args.get("override") else ""
1408        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1410    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1411        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1412        statistics = expression.args.get("statistics")
1413        statistics_sql = ""
1414        if statistics is not None:
1415            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1416        return f"{data_sql}{statistics_sql}"
def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1418    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1419        sql = "WITH(SYSTEM_VERSIONING=ON"
1420
1421        if expression.this:
1422            history_table = self.sql(expression, "this")
1423            sql = f"{sql}(HISTORY_TABLE={history_table}"
1424
1425            if expression.expression:
1426                data_consistency_check = self.sql(expression, "expression")
1427                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
1428
1429            sql = f"{sql})"
1430
1431        return f"{sql})"
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1433    def insert_sql(self, expression: exp.Insert) -> str:
1434        overwrite = expression.args.get("overwrite")
1435
1436        if isinstance(expression.this, exp.Directory):
1437            this = " OVERWRITE" if overwrite else " INTO"
1438        else:
1439            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1440
1441        alternative = expression.args.get("alternative")
1442        alternative = f" OR {alternative}" if alternative else ""
1443        ignore = " IGNORE" if expression.args.get("ignore") else ""
1444
1445        this = f"{this} {self.sql(expression, 'this')}"
1446
1447        exists = " IF EXISTS" if expression.args.get("exists") else ""
1448        partition_sql = (
1449            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1450        )
1451        where = self.sql(expression, "where")
1452        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1453        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1454        conflict = self.sql(expression, "conflict")
1455        by_name = " BY NAME" if expression.args.get("by_name") else ""
1456        returning = self.sql(expression, "returning")
1457
1458        if self.RETURNING_END:
1459            expression_sql = f"{expression_sql}{conflict}{returning}"
1460        else:
1461            expression_sql = f"{returning}{expression_sql}{conflict}"
1462
1463        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1464        return self.prepend_ctes(expression, sql)
def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str:
1466    def intersect_sql(self, expression: exp.Intersect) -> str:
1467        return self.prepend_ctes(
1468            expression,
1469            self.set_operation(expression, self.intersect_op(expression)),
1470        )
def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str:
1472    def intersect_op(self, expression: exp.Intersect) -> str:
1473        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1475    def introducer_sql(self, expression: exp.Introducer) -> str:
1476        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def kill_sql(self, expression: sqlglot.expressions.Kill) -> str:
1478    def kill_sql(self, expression: exp.Kill) -> str:
1479        kind = self.sql(expression, "kind")
1480        kind = f" {kind}" if kind else ""
1481        this = self.sql(expression, "this")
1482        this = f" {this}" if this else ""
1483        return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1485    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1486        return expression.name
def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str:
1488    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1489        return expression.name
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1491    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1492        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1493        constraint = self.sql(expression, "constraint")
1494        if constraint:
1495            constraint = f"ON CONSTRAINT {constraint}"
1496        key = self.expressions(expression, key="key", flat=True)
1497        do = "" if expression.args.get("duplicate") else " DO "
1498        nothing = "NOTHING" if expression.args.get("nothing") else ""
1499        expressions = self.expressions(expression, flat=True)
1500        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1501        if expressions:
1502            expressions = f"UPDATE {set_keyword}{expressions}"
1503        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1505    def returning_sql(self, expression: exp.Returning) -> str:
1506        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1508    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1509        fields = expression.args.get("fields")
1510        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1511        escaped = expression.args.get("escaped")
1512        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1513        items = expression.args.get("collection_items")
1514        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1515        keys = expression.args.get("map_keys")
1516        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1517        lines = expression.args.get("lines")
1518        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1519        null = expression.args.get("null")
1520        null = f" NULL DEFINED AS {null}" if null else ""
1521        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str:
1523    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1524        return f"WITH ({self.expressions(expression, flat=True)})"
def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str:
1526    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1527        this = f"{self.sql(expression, 'this')} INDEX"
1528        target = self.sql(expression, "target")
1529        target = f" FOR {target}" if target else ""
1530        return f"{this}{target} ({self.expressions(expression, flat=True)})"
def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str:
1532    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1533        this = self.sql(expression, "this")
1534        kind = self.sql(expression, "kind")
1535        expr = self.sql(expression, "expression")
1536        return f"{this} ({kind} => {expr})"
def table_parts(self, expression: sqlglot.expressions.Table) -> str:
1538    def table_parts(self, expression: exp.Table) -> str:
1539        return ".".join(
1540            self.sql(part)
1541            for part in (
1542                expression.args.get("catalog"),
1543                expression.args.get("db"),
1544                expression.args.get("this"),
1545            )
1546            if part is not None
1547        )
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
1549    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1550        table = self.table_parts(expression)
1551        only = "ONLY " if expression.args.get("only") else ""
1552        version = self.sql(expression, "version")
1553        version = f" {version}" if version else ""
1554        alias = self.sql(expression, "alias")
1555        alias = f"{sep}{alias}" if alias else ""
1556        hints = self.expressions(expression, key="hints", sep=" ")
1557        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1558        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1559        pivots = f" {pivots}" if pivots else ""
1560        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1561        laterals = self.expressions(expression, key="laterals", sep="")
1562
1563        file_format = self.sql(expression, "format")
1564        if file_format:
1565            pattern = self.sql(expression, "pattern")
1566            pattern = f", PATTERN => {pattern}" if pattern else ""
1567            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1568
1569        ordinality = expression.args.get("ordinality") or ""
1570        if ordinality:
1571            ordinality = f" WITH ORDINALITY{alias}"
1572            alias = ""
1573
1574        when = self.sql(expression, "when")
1575        if when:
1576            table = f"{table} {when}"
1577
1578        return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
def tablesample_sql( self, expression: sqlglot.expressions.TableSample, sep: str = ' AS ', tablesample_keyword: Optional[str] = None) -> str:
1580    def tablesample_sql(
1581        self,
1582        expression: exp.TableSample,
1583        sep: str = " AS ",
1584        tablesample_keyword: t.Optional[str] = None,
1585    ) -> str:
1586        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1587            table = expression.this.copy()
1588            table.set("alias", None)
1589            this = self.sql(table)
1590            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1591        else:
1592            this = self.sql(expression, "this")
1593            alias = ""
1594
1595        method = self.sql(expression, "method")
1596        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1597        numerator = self.sql(expression, "bucket_numerator")
1598        denominator = self.sql(expression, "bucket_denominator")
1599        field = self.sql(expression, "bucket_field")
1600        field = f" ON {field}" if field else ""
1601        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1602        seed = self.sql(expression, "seed")
1603        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1604
1605        size = self.sql(expression, "size")
1606        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1607            size = f"{size} ROWS"
1608
1609        percent = self.sql(expression, "percent")
1610        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1611            percent = f"{percent} PERCENT"
1612
1613        expr = f"{bucket}{percent}{size}"
1614        if self.TABLESAMPLE_REQUIRES_PARENS:
1615            expr = f"({expr})"
1616
1617        return (
1618            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1619        )
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
1621    def pivot_sql(self, expression: exp.Pivot) -> str:
1622        expressions = self.expressions(expression, flat=True)
1623
1624        if expression.this:
1625            this = self.sql(expression, "this")
1626            if not expressions:
1627                return f"UNPIVOT {this}"
1628
1629            on = f"{self.seg('ON')} {expressions}"
1630            using = self.expressions(expression, key="using", flat=True)
1631            using = f"{self.seg('USING')} {using}" if using else ""
1632            group = self.sql(expression, "group")
1633            return f"PIVOT {this}{on}{using}{group}"
1634
1635        alias = self.sql(expression, "alias")
1636        alias = f" AS {alias}" if alias else ""
1637        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1638        field = self.sql(expression, "field")
1639        include_nulls = expression.args.get("include_nulls")
1640        if include_nulls is not None:
1641            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1642        else:
1643            nulls = ""
1644        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
def version_sql(self, expression: sqlglot.expressions.Version) -> str:
1646    def version_sql(self, expression: exp.Version) -> str:
1647        this = f"FOR {expression.name}"
1648        kind = expression.text("kind")
1649        expr = self.sql(expression, "expression")
1650        return f"{this} {kind} {expr}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
1652    def tuple_sql(self, expression: exp.Tuple) -> str:
1653        return f"({self.expressions(expression, flat=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
1655    def update_sql(self, expression: exp.Update) -> str:
1656        this = self.sql(expression, "this")
1657        set_sql = self.expressions(expression, flat=True)
1658        from_sql = self.sql(expression, "from")
1659        where_sql = self.sql(expression, "where")
1660        returning = self.sql(expression, "returning")
1661        order = self.sql(expression, "order")
1662        limit = self.sql(expression, "limit")
1663        if self.RETURNING_END:
1664            expression_sql = f"{from_sql}{where_sql}{returning}"
1665        else:
1666            expression_sql = f"{returning}{from_sql}{where_sql}"
1667        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1668        return self.prepend_ctes(expression, sql)
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
1670    def values_sql(self, expression: exp.Values) -> str:
1671        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1672        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1673            args = self.expressions(expression)
1674            alias = self.sql(expression, "alias")
1675            values = f"VALUES{self.seg('')}{args}"
1676            values = (
1677                f"({values})"
1678                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1679                else values
1680            )
1681            return f"{values} AS {alias}" if alias else values
1682
1683        # Converts `VALUES...` expression into a series of select unions.
1684        alias_node = expression.args.get("alias")
1685        column_names = alias_node and alias_node.columns
1686
1687        selects: t.List[exp.Query] = []
1688
1689        for i, tup in enumerate(expression.expressions):
1690            row = tup.expressions
1691
1692            if i == 0 and column_names:
1693                row = [
1694                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1695                ]
1696
1697            selects.append(exp.Select(expressions=row))
1698
1699        if self.pretty:
1700            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1701            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1702            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1703            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1704            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
1705
1706        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
1707        unions = " UNION ALL ".join(self.sql(select) for select in selects)
1708        return f"({unions}){alias}"
def var_sql(self, expression: sqlglot.expressions.Var) -> str:
1710    def var_sql(self, expression: exp.Var) -> str:
1711        return self.sql(expression, "this")
def into_sql(self, expression: sqlglot.expressions.Into) -> str:
1713    def into_sql(self, expression: exp.Into) -> str:
1714        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1715        unlogged = " UNLOGGED" if expression.args.get("unlogged") else ""
1716        return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}"
def from_sql(self, expression: sqlglot.expressions.From) -> str:
1718    def from_sql(self, expression: exp.From) -> str:
1719        return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
def group_sql(self, expression: sqlglot.expressions.Group) -> str:
1721    def group_sql(self, expression: exp.Group) -> str:
1722        group_by = self.op_expressions("GROUP BY", expression)
1723
1724        if expression.args.get("all"):
1725            return f"{group_by} ALL"
1726
1727        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
1728        grouping_sets = (
1729            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
1730        )
1731
1732        cube = expression.args.get("cube", [])
1733        if seq_get(cube, 0) is True:
1734            return f"{group_by}{self.seg('WITH CUBE')}"
1735        else:
1736            cube_sql = self.expressions(expression, key="cube", indent=False)
1737            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
1738
1739        rollup = expression.args.get("rollup", [])
1740        if seq_get(rollup, 0) is True:
1741            return f"{group_by}{self.seg('WITH ROLLUP')}"
1742        else:
1743            rollup_sql = self.expressions(expression, key="rollup", indent=False)
1744            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
1745
1746        groupings = csv(
1747            grouping_sets,
1748            cube_sql,
1749            rollup_sql,
1750            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
1751            sep=self.GROUPINGS_SEP,
1752        )
1753
1754        if expression.args.get("expressions") and groupings:
1755            group_by = f"{group_by}{self.GROUPINGS_SEP}"
1756
1757        return f"{group_by}{groupings}"
def having_sql(self, expression: sqlglot.expressions.Having) -> str:
1759    def having_sql(self, expression: exp.Having) -> str:
1760        this = self.indent(self.sql(expression, "this"))
1761        return f"{self.seg('HAVING')}{self.sep()}{this}"
def connect_sql(self, expression: sqlglot.expressions.Connect) -> str:
1763    def connect_sql(self, expression: exp.Connect) -> str:
1764        start = self.sql(expression, "start")
1765        start = self.seg(f"START WITH {start}") if start else ""
1766        connect = self.sql(expression, "connect")
1767        connect = self.seg(f"CONNECT BY {connect}")
1768        return start + connect
def prior_sql(self, expression: sqlglot.expressions.Prior) -> str:
1770    def prior_sql(self, expression: exp.Prior) -> str:
1771        return f"PRIOR {self.sql(expression, 'this')}"
def join_sql(self, expression: sqlglot.expressions.Join) -> str:
1773    def join_sql(self, expression: exp.Join) -> str:
1774        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
1775            side = None
1776        else:
1777            side = expression.side
1778
1779        op_sql = " ".join(
1780            op
1781            for op in (
1782                expression.method,
1783                "GLOBAL" if expression.args.get("global") else None,
1784                side,
1785                expression.kind,
1786                expression.hint if self.JOIN_HINTS else None,
1787            )
1788            if op
1789        )
1790        on_sql = self.sql(expression, "on")
1791        using = expression.args.get("using")
1792
1793        if not on_sql and using:
1794            on_sql = csv(*(self.sql(column) for column in using))
1795
1796        this = expression.this
1797        this_sql = self.sql(this)
1798
1799        if on_sql:
1800            on_sql = self.indent(on_sql, skip_first=True)
1801            space = self.seg(" " * self.pad) if self.pretty else " "
1802            if using:
1803                on_sql = f"{space}USING ({on_sql})"
1804            else:
1805                on_sql = f"{space}ON {on_sql}"
1806        elif not op_sql:
1807            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
1808                return f" {this_sql}"
1809
1810            return f", {this_sql}"
1811
1812        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
1813        return f"{self.seg(op_sql)} {this_sql}{on_sql}"
def lambda_sql( self, expression: sqlglot.expressions.Lambda, arrow_sep: str = '->') -> str:
1815    def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str:
1816        args = self.expressions(expression, flat=True)
1817        args = f"({args})" if len(args.split(",")) > 1 else args
1818        return f"{args} {arrow_sep} {self.sql(expression, 'this')}"
def lateral_op(self, expression: sqlglot.expressions.Lateral) -> str:
1820    def lateral_op(self, expression: exp.Lateral) -> str:
1821        cross_apply = expression.args.get("cross_apply")
1822
1823        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
1824        if cross_apply is True:
1825            op = "INNER JOIN "
1826        elif cross_apply is False:
1827            op = "LEFT JOIN "
1828        else:
1829            op = ""
1830
1831        return f"{op}LATERAL"
def lateral_sql(self, expression: sqlglot.expressions.Lateral) -> str:
1833    def lateral_sql(self, expression: exp.Lateral) -> str:
1834        this = self.sql(expression, "this")
1835
1836        if expression.args.get("view"):
1837            alias = expression.args["alias"]
1838            columns = self.expressions(alias, key="columns", flat=True)
1839            table = f" {alias.name}" if alias.name else ""
1840            columns = f" AS {columns}" if columns else ""
1841            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
1842            return f"{op_sql}{self.sep()}{this}{table}{columns}"
1843
1844        alias = self.sql(expression, "alias")
1845        alias = f" AS {alias}" if alias else ""
1846        return f"{self.lateral_op(expression)} {this}{alias}"
def limit_sql(self, expression: sqlglot.expressions.Limit, top: bool = False) -> str:
1848    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
1849        this = self.sql(expression, "this")
1850
1851        args = [
1852            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
1853            for e in (expression.args.get(k) for k in ("offset", "expression"))
1854            if e
1855        ]
1856
1857        args_sql = ", ".join(self.sql(e) for e in args)
1858        args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql
1859        expressions = self.expressions(expression, flat=True)
1860        expressions = f" BY {expressions}" if expressions else ""
1861
1862        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
def offset_sql(self, expression: sqlglot.expressions.Offset) -> str:
1864    def offset_sql(self, expression: exp.Offset) -> str:
1865        this = self.sql(expression, "this")
1866        value = expression.expression
1867        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
1868        expressions = self.expressions(expression, flat=True)
1869        expressions = f" BY {expressions}" if expressions else ""
1870        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
def setitem_sql(self, expression: sqlglot.expressions.SetItem) -> str:
1872    def setitem_sql(self, expression: exp.SetItem) -> str:
1873        kind = self.sql(expression, "kind")
1874        kind = f"{kind} " if kind else ""
1875        this = self.sql(expression, "this")
1876        expressions = self.expressions(expression)
1877        collate = self.sql(expression, "collate")
1878        collate = f" COLLATE {collate}" if collate else ""
1879        global_ = "GLOBAL " if expression.args.get("global") else ""
1880        return f"{global_}{kind}{this}{expressions}{collate}"
def set_sql(self, expression: sqlglot.expressions.Set) -> str:
1882    def set_sql(self, expression: exp.Set) -> str:
1883        expressions = (
1884            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
1885        )
1886        tag = " TAG" if expression.args.get("tag") else ""
1887        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
def pragma_sql(self, expression: sqlglot.expressions.Pragma) -> str:
1889    def pragma_sql(self, expression: exp.Pragma) -> str:
1890        return f"PRAGMA {self.sql(expression, 'this')}"
def lock_sql(self, expression: sqlglot.expressions.Lock) -> str:
1892    def lock_sql(self, expression: exp.Lock) -> str:
1893        if not self.LOCKING_READS_SUPPORTED:
1894            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
1895            return ""
1896
1897        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
1898        expressions = self.expressions(expression, flat=True)
1899        expressions = f" OF {expressions}" if expressions else ""
1900        wait = expression.args.get("wait")
1901
1902        if wait is not None:
1903            if isinstance(wait, exp.Literal):
1904                wait = f" WAIT {self.sql(wait)}"
1905            else:
1906                wait = " NOWAIT" if wait else " SKIP LOCKED"
1907
1908        return f"{lock_type}{expressions}{wait or ''}"
def literal_sql(self, expression: sqlglot.expressions.Literal) -> str:
1910    def literal_sql(self, expression: exp.Literal) -> str:
1911        text = expression.this or ""
1912        if expression.is_string:
1913            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
1914        return text
def escape_str(self, text: str) -> str:
1916    def escape_str(self, text: str) -> str:
1917        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
1918        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
1919            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
1920        elif self.pretty:
1921            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
1922        return text
def loaddata_sql(self, expression: sqlglot.expressions.LoadData) -> str:
1924    def loaddata_sql(self, expression: exp.LoadData) -> str:
1925        local = " LOCAL" if expression.args.get("local") else ""
1926        inpath = f" INPATH {self.sql(expression, 'inpath')}"
1927        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
1928        this = f" INTO TABLE {self.sql(expression, 'this')}"
1929        partition = self.sql(expression, "partition")
1930        partition = f" {partition}" if partition else ""
1931        input_format = self.sql(expression, "input_format")
1932        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
1933        serde = self.sql(expression, "serde")
1934        serde = f" SERDE {serde}" if serde else ""
1935        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
def null_sql(self, *_) -> str:
1937    def null_sql(self, *_) -> str:
1938        return "NULL"
def boolean_sql(self, expression: sqlglot.expressions.Boolean) -> str:
1940    def boolean_sql(self, expression: exp.Boolean) -> str:
1941        return "TRUE" if expression.this else "FALSE"
def order_sql(self, expression: sqlglot.expressions.Order, flat: bool = False) -> str:
1943    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
1944        this = self.sql(expression, "this")
1945        this = f"{this} " if this else this
1946        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
1947        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
1948        interpolated_values = [
1949            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
1950            for named_expression in expression.args.get("interpolate") or []
1951        ]
1952        interpolate = (
1953            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
1954        )
1955        return f"{order}{interpolate}"
def withfill_sql(self, expression: sqlglot.expressions.WithFill) -> str:
1957    def withfill_sql(self, expression: exp.WithFill) -> str:
1958        from_sql = self.sql(expression, "from")
1959        from_sql = f" FROM {from_sql}" if from_sql else ""
1960        to_sql = self.sql(expression, "to")
1961        to_sql = f" TO {to_sql}" if to_sql else ""
1962        step_sql = self.sql(expression, "step")
1963        step_sql = f" STEP {step_sql}" if step_sql else ""
1964        return f"WITH FILL{from_sql}{to_sql}{step_sql}"
def cluster_sql(self, expression: sqlglot.expressions.Cluster) -> str:
1966    def cluster_sql(self, expression: exp.Cluster) -> str:
1967        return self.op_expressions("CLUSTER BY", expression)
def distribute_sql(self, expression: sqlglot.expressions.Distribute) -> str:
1969    def distribute_sql(self, expression: exp.Distribute) -> str:
1970        return self.op_expressions("DISTRIBUTE BY", expression)
def sort_sql(self, expression: sqlglot.expressions.Sort) -> str:
1972    def sort_sql(self, expression: exp.Sort) -> str:
1973        return self.op_expressions("SORT BY", expression)
def ordered_sql(self, expression: sqlglot.expressions.Ordered) -> str:
1975    def ordered_sql(self, expression: exp.Ordered) -> str:
1976        desc = expression.args.get("desc")
1977        asc = not desc
1978
1979        nulls_first = expression.args.get("nulls_first")
1980        nulls_last = not nulls_first
1981        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
1982        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
1983        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
1984
1985        this = self.sql(expression, "this")
1986
1987        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
1988        nulls_sort_change = ""
1989        if nulls_first and (
1990            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
1991        ):
1992            nulls_sort_change = " NULLS FIRST"
1993        elif (
1994            nulls_last
1995            and ((asc and nulls_are_small) or (desc and nulls_are_large))
1996            and not nulls_are_last
1997        ):
1998            nulls_sort_change = " NULLS LAST"
1999
2000        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
2001        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
2002            window = expression.find_ancestor(exp.Window, exp.Select)
2003            if isinstance(window, exp.Window) and window.args.get("spec"):
2004                self.unsupported(
2005                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
2006                )
2007                nulls_sort_change = ""
2008            elif self.NULL_ORDERING_SUPPORTED is None:
2009                if expression.this.is_int:
2010                    self.unsupported(
2011                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
2012                    )
2013                else:
2014                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
2015                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
2016                nulls_sort_change = ""
2017
2018        with_fill = self.sql(expression, "with_fill")
2019        with_fill = f" {with_fill}" if with_fill else ""
2020
2021        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
def matchrecognize_sql(self, expression: sqlglot.expressions.MatchRecognize) -> str:
2023    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
2024        partition = self.partition_by_sql(expression)
2025        order = self.sql(expression, "order")
2026        measures = self.expressions(expression, key="measures")
2027        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
2028        rows = self.sql(expression, "rows")
2029        rows = self.seg(rows) if rows else ""
2030        after = self.sql(expression, "after")
2031        after = self.seg(after) if after else ""
2032        pattern = self.sql(expression, "pattern")
2033        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
2034        definition_sqls = [
2035            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
2036            for definition in expression.args.get("define", [])
2037        ]
2038        definitions = self.expressions(sqls=definition_sqls)
2039        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
2040        body = "".join(
2041            (
2042                partition,
2043                order,
2044                measures,
2045                rows,
2046                after,
2047                pattern,
2048                define,
2049            )
2050        )
2051        alias = self.sql(expression, "alias")
2052        alias = f" {alias}" if alias else ""
2053        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
def query_modifiers(self, expression: sqlglot.expressions.Expression, *sqls: str) -> str:
2055    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
2056        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
2057
2058        # If the limit is generated as TOP, we need to ensure it's not generated twice
2059        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
2060
2061        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
2062            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
2063        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
2064            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
2065
2066        fetch = isinstance(limit, exp.Fetch)
2067
2068        offset_limit_modifiers = (
2069            self.offset_limit_modifiers(expression, fetch, limit)
2070            if with_offset_limit_modifiers
2071            else []
2072        )
2073
2074        options = self.expressions(expression, key="options")
2075        if options:
2076            options = f" OPTION{self.wrap(options)}"
2077
2078        return csv(
2079            *sqls,
2080            *[self.sql(join) for join in expression.args.get("joins") or []],
2081            self.sql(expression, "connect"),
2082            self.sql(expression, "match"),
2083            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2084            self.sql(expression, "prewhere"),
2085            self.sql(expression, "where"),
2086            self.sql(expression, "group"),
2087            self.sql(expression, "having"),
2088            *self.after_having_modifiers(expression),
2089            self.sql(expression, "order"),
2090            *offset_limit_modifiers,
2091            *self.after_limit_modifiers(expression),
2092            options,
2093            sep="",
2094        )
def queryoption_sql(self, expression: sqlglot.expressions.QueryOption) -> str:
2096    def queryoption_sql(self, expression: exp.QueryOption) -> str:
2097        return ""
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2099    def offset_limit_modifiers(
2100        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2101    ) -> t.List[str]:
2102        return [
2103            self.sql(expression, "offset") if fetch else self.sql(limit),
2104            self.sql(limit) if fetch else self.sql(expression, "offset"),
2105        ]
def after_having_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2107    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
2108        return [
2109            self.sql(expression, "qualify"),
2110            (
2111                self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
2112                if expression.args.get("windows")
2113                else ""
2114            ),
2115            self.sql(expression, "distribute"),
2116            self.sql(expression, "sort"),
2117            self.sql(expression, "cluster"),
2118        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2120    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2121        locks = self.expressions(expression, key="locks", sep=" ")
2122        locks = f" {locks}" if locks else ""
2123        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
2125    def select_sql(self, expression: exp.Select) -> str:
2126        into = expression.args.get("into")
2127        if not self.SUPPORTS_SELECT_INTO and into:
2128            into.pop()
2129
2130        hint = self.sql(expression, "hint")
2131        distinct = self.sql(expression, "distinct")
2132        distinct = f" {distinct}" if distinct else ""
2133        kind = self.sql(expression, "kind")
2134        limit = expression.args.get("limit")
2135        top = (
2136            self.limit_sql(limit, top=True)
2137            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2138            else ""
2139        )
2140
2141        expressions = self.expressions(expression)
2142
2143        if kind:
2144            if kind in self.SELECT_KINDS:
2145                kind = f" AS {kind}"
2146            else:
2147                if kind == "STRUCT":
2148                    expressions = self.expressions(
2149                        sqls=[
2150                            self.sql(
2151                                exp.Struct(
2152                                    expressions=[
2153                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
2154                                        if isinstance(e, exp.Alias)
2155                                        else e
2156                                        for e in expression.expressions
2157                                    ]
2158                                )
2159                            )
2160                        ]
2161                    )
2162                kind = ""
2163
2164        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2165        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2166        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2167        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2168        sql = self.query_modifiers(
2169            expression,
2170            f"SELECT{top_distinct}{kind}{expressions}",
2171            self.sql(expression, "into", comment=False),
2172            self.sql(expression, "from", comment=False),
2173        )
2174
2175        sql = self.prepend_ctes(expression, sql)
2176
2177        if not self.SUPPORTS_SELECT_INTO and into:
2178            if into.args.get("temporary"):
2179                table_kind = " TEMPORARY"
2180            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2181                table_kind = " UNLOGGED"
2182            else:
2183                table_kind = ""
2184            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2185
2186        return sql
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
2188    def schema_sql(self, expression: exp.Schema) -> str:
2189        this = self.sql(expression, "this")
2190        sql = self.schema_columns_sql(expression)
2191        return f"{this} {sql}" if this and sql else this or sql
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
2193    def schema_columns_sql(self, expression: exp.Schema) -> str:
2194        if expression.expressions:
2195            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2196        return ""
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
2198    def star_sql(self, expression: exp.Star) -> str:
2199        except_ = self.expressions(expression, key="except", flat=True)
2200        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2201        replace = self.expressions(expression, key="replace", flat=True)
2202        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2203        return f"*{except_}{replace}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
2205    def parameter_sql(self, expression: exp.Parameter) -> str:
2206        this = self.sql(expression, "this")
2207        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
2209    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2210        this = self.sql(expression, "this")
2211        kind = expression.text("kind")
2212        if kind:
2213            kind = f"{kind}."
2214        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
2216    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2217        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
2219    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2220        alias = self.sql(expression, "alias")
2221        alias = f"{sep}{alias}" if alias else ""
2222
2223        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2224        pivots = f" {pivots}" if pivots else ""
2225
2226        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2227        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
2229    def qualify_sql(self, expression: exp.Qualify) -> str:
2230        this = self.indent(self.sql(expression, "this"))
2231        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def union_sql(self, expression: sqlglot.expressions.Union) -> str:
2233    def union_sql(self, expression: exp.Union) -> str:
2234        return self.prepend_ctes(
2235            expression,
2236            self.set_operation(expression, self.union_op(expression)),
2237        )
def union_op(self, expression: sqlglot.expressions.Union) -> str:
2239    def union_op(self, expression: exp.Union) -> str:
2240        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2241        kind = kind if expression.args.get("distinct") else " ALL"
2242        by_name = " BY NAME" if expression.args.get("by_name") else ""
2243        return f"UNION{kind}{by_name}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
2245    def unnest_sql(self, expression: exp.Unnest) -> str:
2246        args = self.expressions(expression, flat=True)
2247
2248        alias = expression.args.get("alias")
2249        offset = expression.args.get("offset")
2250
2251        if self.UNNEST_WITH_ORDINALITY:
2252            if alias and isinstance(offset, exp.Expression):
2253                alias.append("columns", offset)
2254
2255        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2256            columns = alias.columns
2257            alias = self.sql(columns[0]) if columns else ""
2258        else:
2259            alias = self.sql(alias)
2260
2261        alias = f" AS {alias}" if alias else alias
2262        if self.UNNEST_WITH_ORDINALITY:
2263            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2264        else:
2265            if isinstance(offset, exp.Expression):
2266                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2267            elif offset:
2268                suffix = f"{alias} WITH OFFSET"
2269            else:
2270                suffix = alias
2271
2272        return f"UNNEST({args}){suffix}"
def prewhere_sql(self, expression: sqlglot.expressions.PreWhere) -> str:
2274    def prewhere_sql(self, expression: exp.PreWhere) -> str:
2275        return ""
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
2277    def where_sql(self, expression: exp.Where) -> str:
2278        this = self.indent(self.sql(expression, "this"))
2279        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
2281    def window_sql(self, expression: exp.Window) -> str:
2282        this = self.sql(expression, "this")
2283        partition = self.partition_by_sql(expression)
2284        order = expression.args.get("order")
2285        order = self.order_sql(order, flat=True) if order else ""
2286        spec = self.sql(expression, "spec")
2287        alias = self.sql(expression, "alias")
2288        over = self.sql(expression, "over") or "OVER"
2289
2290        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2291
2292        first = expression.args.get("first")
2293        if first is None:
2294            first = ""
2295        else:
2296            first = "FIRST" if first else "LAST"
2297
2298        if not partition and not order and not spec and alias:
2299            return f"{this} {alias}"
2300
2301        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2302        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2304    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2305        partition = self.expressions(expression, key="partition_by", flat=True)
2306        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
2308    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2309        kind = self.sql(expression, "kind")
2310        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2311        end = (
2312            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2313            or "CURRENT ROW"
2314        )
2315        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
2317    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2318        this = self.sql(expression, "this")
2319        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2320        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
2322    def between_sql(self, expression: exp.Between) -> str:
2323        this = self.sql(expression, "this")
2324        low = self.sql(expression, "low")
2325        high = self.sql(expression, "high")
2326        return f"{this} BETWEEN {low} AND {high}"
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
2328    def bracket_sql(self, expression: exp.Bracket) -> str:
2329        expressions = apply_index_offset(
2330            expression.this,
2331            expression.expressions,
2332            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2333        )
2334        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2335        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
2337    def all_sql(self, expression: exp.All) -> str:
2338        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
2340    def any_sql(self, expression: exp.Any) -> str:
2341        this = self.sql(expression, "this")
2342        if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
2343            this = self.wrap(this)
2344        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
2346    def exists_sql(self, expression: exp.Exists) -> str:
2347        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
2349    def case_sql(self, expression: exp.Case) -> str:
2350        this = self.sql(expression, "this")
2351        statements = [f"CASE {this}" if this else "CASE"]
2352
2353        for e in expression.args["ifs"]:
2354            statements.append(f"WHEN {self.sql(e, 'this')}")
2355            statements.append(f"THEN {self.sql(e, 'true')}")
2356
2357        default = self.sql(expression, "default")
2358
2359        if default:
2360            statements.append(f"ELSE {default}")
2361
2362        statements.append("END")
2363
2364        if self.pretty and self.text_width(statements) > self.max_text_width:
2365            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2366
2367        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
2369    def constraint_sql(self, expression: exp.Constraint) -> str:
2370        this = self.sql(expression, "this")
2371        expressions = self.expressions(expression, flat=True)
2372        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
2374    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2375        order = expression.args.get("order")
2376        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2377        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
2379    def extract_sql(self, expression: exp.Extract) -> str:
2380        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2381        expression_sql = self.sql(expression, "expression")
2382        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
2384    def trim_sql(self, expression: exp.Trim) -> str:
2385        trim_type = self.sql(expression, "position")
2386
2387        if trim_type == "LEADING":
2388            return self.func("LTRIM", expression.this)
2389        elif trim_type == "TRAILING":
2390            return self.func("RTRIM", expression.this)
2391        else:
2392            return self.func("TRIM", expression.this, expression.expression)
def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2394    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2395        args = expression.expressions
2396        if isinstance(expression, exp.ConcatWs):
2397            args = args[1:]  # Skip the delimiter
2398
2399        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2400            args = [exp.cast(e, "text") for e in args]
2401
2402        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2403            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2404
2405        return args
def concat_sql(self, expression: sqlglot.expressions.Concat) -> str:
2407    def concat_sql(self, expression: exp.Concat) -> str:
2408        expressions = self.convert_concat_args(expression)
2409
2410        # Some dialects don't allow a single-argument CONCAT call
2411        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2412            return self.sql(expressions[0])
2413
2414        return self.func("CONCAT", *expressions)
def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str:
2416    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2417        return self.func(
2418            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2419        )
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
2421    def check_sql(self, expression: exp.Check) -> str:
2422        this = self.sql(expression, key="this")
2423        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2425    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2426        expressions = self.expressions(expression, flat=True)
2427        reference = self.sql(expression, "reference")
2428        reference = f" {reference}" if reference else ""
2429        delete = self.sql(expression, "delete")
2430        delete = f" ON DELETE {delete}" if delete else ""
2431        update = self.sql(expression, "update")
2432        update = f" ON UPDATE {update}" if update else ""
2433        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2435    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2436        expressions = self.expressions(expression, flat=True)
2437        options = self.expressions(expression, key="options", flat=True, sep=" ")
2438        options = f" {options}" if options else ""
2439        return f"PRIMARY KEY ({expressions}){options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
2441    def if_sql(self, expression: exp.If) -> str:
2442        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
2444    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2445        modifier = expression.args.get("modifier")
2446        modifier = f" {modifier}" if modifier else ""
2447        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
2449    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2450        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:
2452    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2453        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2454        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str:
2456    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2457        if isinstance(expression, exp.JSONPathPart):
2458            transform = self.TRANSFORMS.get(expression.__class__)
2459            if not callable(transform):
2460                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2461                return ""
2462
2463            return transform(self, expression)
2464
2465        if isinstance(expression, int):
2466            return str(expression)
2467
2468        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2469            escaped = expression.replace("'", "\\'")
2470            escaped = f"\\'{expression}\\'"
2471        else:
2472            escaped = expression.replace('"', '\\"')
2473            escaped = f'"{escaped}"'
2474
2475        return escaped
def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str:
2477    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2478        return f"{self.sql(expression, 'this')} FORMAT JSON"
def jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2480    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2481        null_handling = expression.args.get("null_handling")
2482        null_handling = f" {null_handling}" if null_handling else ""
2483
2484        unique_keys = expression.args.get("unique_keys")
2485        if unique_keys is not None:
2486            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2487        else:
2488            unique_keys = ""
2489
2490        return_type = self.sql(expression, "return_type")
2491        return_type = f" RETURNING {return_type}" if return_type else ""
2492        encoding = self.sql(expression, "encoding")
2493        encoding = f" ENCODING {encoding}" if encoding else ""
2494
2495        return self.func(
2496            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2497            *expression.expressions,
2498            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2499        )
def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str:
2501    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2502        return self.jsonobject_sql(expression)
def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str:
2504    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2505        null_handling = expression.args.get("null_handling")
2506        null_handling = f" {null_handling}" if null_handling else ""
2507        return_type = self.sql(expression, "return_type")
2508        return_type = f" RETURNING {return_type}" if return_type else ""
2509        strict = " STRICT" if expression.args.get("strict") else ""
2510        return self.func(
2511            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2512        )
def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str:
2514    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2515        this = self.sql(expression, "this")
2516        order = self.sql(expression, "order")
2517        null_handling = expression.args.get("null_handling")
2518        null_handling = f" {null_handling}" if null_handling else ""
2519        return_type = self.sql(expression, "return_type")
2520        return_type = f" RETURNING {return_type}" if return_type else ""
2521        strict = " STRICT" if expression.args.get("strict") else ""
2522        return self.func(
2523            "JSON_ARRAYAGG",
2524            this,
2525            suffix=f"{order}{null_handling}{return_type}{strict})",
2526        )
def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str:
2528    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2529        path = self.sql(expression, "path")
2530        path = f" PATH {path}" if path else ""
2531        nested_schema = self.sql(expression, "nested_schema")
2532
2533        if nested_schema:
2534            return f"NESTED{path} {nested_schema}"
2535
2536        this = self.sql(expression, "this")
2537        kind = self.sql(expression, "kind")
2538        kind = f" {kind}" if kind else ""
2539        return f"{this}{kind}{path}"
def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str:
2541    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2542        return self.func("COLUMNS", *expression.expressions)
def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str:
2544    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2545        this = self.sql(expression, "this")
2546        path = self.sql(expression, "path")
2547        path = f", {path}" if path else ""
2548        error_handling = expression.args.get("error_handling")
2549        error_handling = f" {error_handling}" if error_handling else ""
2550        empty_handling = expression.args.get("empty_handling")
2551        empty_handling = f" {empty_handling}" if empty_handling else ""
2552        schema = self.sql(expression, "schema")
2553        return self.func(
2554            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2555        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
2557    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2558        this = self.sql(expression, "this")
2559        kind = self.sql(expression, "kind")
2560        path = self.sql(expression, "path")
2561        path = f" {path}" if path else ""
2562        as_json = " AS JSON" if expression.args.get("as_json") else ""
2563        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
2565    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2566        this = self.sql(expression, "this")
2567        path = self.sql(expression, "path")
2568        path = f", {path}" if path else ""
2569        expressions = self.expressions(expression)
2570        with_ = (
2571            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2572            if expressions
2573            else ""
2574        )
2575        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
2577    def in_sql(self, expression: exp.In) -> str:
2578        query = expression.args.get("query")
2579        unnest = expression.args.get("unnest")
2580        field = expression.args.get("field")
2581        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2582
2583        if query:
2584            in_sql = self.wrap(self.sql(query))
2585        elif unnest:
2586            in_sql = self.in_unnest_op(unnest)
2587        elif field:
2588            in_sql = self.sql(field)
2589        else:
2590            in_sql = f"({self.expressions(expression, flat=True)})"
2591
2592        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
2594    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2595        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
2597    def interval_sql(self, expression: exp.Interval) -> str:
2598        unit = self.sql(expression, "unit")
2599        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2600            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2601        unit = f" {unit}" if unit else ""
2602
2603        if self.SINGLE_STRING_INTERVAL:
2604            this = expression.this.name if expression.this else ""
2605            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2606
2607        this = self.sql(expression, "this")
2608        if this:
2609            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2610            this = f" {this}" if unwrapped else f" ({this})"
2611
2612        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
2614    def return_sql(self, expression: exp.Return) -> str:
2615        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
2617    def reference_sql(self, expression: exp.Reference) -> str:
2618        this = self.sql(expression, "this")
2619        expressions = self.expressions(expression, flat=True)
2620        expressions = f"({expressions})" if expressions else ""
2621        options = self.expressions(expression, key="options", flat=True, sep=" ")
2622        options = f" {options}" if options else ""
2623        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
2625    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2626        return self.func(expression.name, *expression.expressions)
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
2628    def paren_sql(self, expression: exp.Paren) -> str:
2629        if isinstance(expression.unnest(), exp.Select):
2630            sql = self.wrap(expression)
2631        else:
2632            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2633            sql = f"({sql}{self.seg(')', sep='')}"
2634
2635        return self.prepend_ctes(expression, sql)
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
2637    def neg_sql(self, expression: exp.Neg) -> str:
2638        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2639        this_sql = self.sql(expression, "this")
2640        sep = " " if this_sql[0] == "-" else ""
2641        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
2643    def not_sql(self, expression: exp.Not) -> str:
2644        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
2646    def alias_sql(self, expression: exp.Alias) -> str:
2647        alias = self.sql(expression, "alias")
2648        alias = f" AS {alias}" if alias else ""
2649        return f"{self.sql(expression, 'this')}{alias}"
def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str:
2651    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2652        alias = expression.args["alias"]
2653        identifier_alias = isinstance(alias, exp.Identifier)
2654
2655        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2656            alias.replace(exp.Literal.string(alias.output_name))
2657        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2658            alias.replace(exp.to_identifier(alias.output_name))
2659
2660        return self.alias_sql(expression)
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
2662    def aliases_sql(self, expression: exp.Aliases) -> str:
2663        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def atindex_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2665    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2666        this = self.sql(expression, "this")
2667        index = self.sql(expression, "expression")
2668        return f"{this} AT {index}"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2670    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2671        this = self.sql(expression, "this")
2672        zone = self.sql(expression, "zone")
2673        return f"{this} AT TIME ZONE {zone}"
def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str:
2675    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2676        this = self.sql(expression, "this")
2677        zone = self.sql(expression, "zone")
2678        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
2680    def add_sql(self, expression: exp.Add) -> str:
2681        return self.binary(expression, "+")
def and_sql(self, expression: sqlglot.expressions.And) -> str:
2683    def and_sql(self, expression: exp.And) -> str:
2684        return self.connector_sql(expression, "AND")
def xor_sql(self, expression: sqlglot.expressions.Xor) -> str:
2686    def xor_sql(self, expression: exp.Xor) -> str:
2687        return self.connector_sql(expression, "XOR")
def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str:
2689    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2690        if not self.pretty:
2691            return self.binary(expression, op)
2692
2693        sqls = tuple(
2694            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2695            for i, e in enumerate(expression.flatten(unnest=False))
2696        )
2697
2698        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2699        return f"{sep}{op} ".join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
2701    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2702        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
2704    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2705        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
2707    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2708        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
2710    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2711        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
2713    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2714        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
2716    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2717        return self.binary(expression, "^")
def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2719    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2720        format_sql = self.sql(expression, "format")
2721        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2722        to_sql = self.sql(expression, "to")
2723        to_sql = f" {to_sql}" if to_sql else ""
2724        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
2726    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2727        zone = self.sql(expression, "this")
2728        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def currenttimestamp_sql(self, expression: sqlglot.expressions.CurrentTimestamp) -> str:
2730    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2731        return self.func("CURRENT_TIMESTAMP", expression.this)
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
2733    def collate_sql(self, expression: exp.Collate) -> str:
2734        if self.COLLATE_IS_FUNC:
2735            return self.function_fallback_sql(expression)
2736        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
2738    def command_sql(self, expression: exp.Command) -> str:
2739        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
2741    def comment_sql(self, expression: exp.Comment) -> str:
2742        this = self.sql(expression, "this")
2743        kind = expression.args["kind"]
2744        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2745        expression_sql = self.sql(expression, "expression")
2746        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
2748    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2749        this = self.sql(expression, "this")
2750        delete = " DELETE" if expression.args.get("delete") else ""
2751        recompress = self.sql(expression, "recompress")
2752        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2753        to_disk = self.sql(expression, "to_disk")
2754        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2755        to_volume = self.sql(expression, "to_volume")
2756        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2757        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
2759    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2760        where = self.sql(expression, "where")
2761        group = self.sql(expression, "group")
2762        aggregates = self.expressions(expression, key="aggregates")
2763        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2764
2765        if not (where or group or aggregates) and len(expression.expressions) == 1:
2766            return f"TTL {self.expressions(expression, flat=True)}"
2767
2768        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
2770    def transaction_sql(self, expression: exp.Transaction) -> str:
2771        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
2773    def commit_sql(self, expression: exp.Commit) -> str:
2774        chain = expression.args.get("chain")
2775        if chain is not None:
2776            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2777
2778        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
2780    def rollback_sql(self, expression: exp.Rollback) -> str:
2781        savepoint = expression.args.get("savepoint")
2782        savepoint = f" TO {savepoint}" if savepoint else ""
2783        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
2785    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2786        this = self.sql(expression, "this")
2787
2788        dtype = self.sql(expression, "dtype")
2789        if dtype:
2790            collate = self.sql(expression, "collate")
2791            collate = f" COLLATE {collate}" if collate else ""
2792            using = self.sql(expression, "using")
2793            using = f" USING {using}" if using else ""
2794            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2795
2796        default = self.sql(expression, "default")
2797        if default:
2798            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2799
2800        comment = self.sql(expression, "comment")
2801        if comment:
2802            return f"ALTER COLUMN {this} COMMENT {comment}"
2803
2804        if not expression.args.get("drop"):
2805            self.unsupported("Unsupported ALTER COLUMN syntax")
2806
2807        return f"ALTER COLUMN {this} DROP DEFAULT"
def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str:
2809    def renametable_sql(self, expression: exp.RenameTable) -> str:
2810        if not self.RENAME_TABLE_WITH_DB:
2811            # Remove db from tables
2812            expression = expression.transform(
2813                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2814            )
2815        this = self.sql(expression, "this")
2816        return f"RENAME TO {this}"
def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str:
2818    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2819        exists = " IF EXISTS" if expression.args.get("exists") else ""
2820        old_column = self.sql(expression, "this")
2821        new_column = self.sql(expression, "to")
2822        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2824    def altertable_sql(self, expression: exp.AlterTable) -> str:
2825        actions = expression.args["actions"]
2826
2827        if isinstance(actions[0], exp.ColumnDef):
2828            actions = self.add_column_sql(expression)
2829        elif isinstance(actions[0], exp.Schema):
2830            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2831        elif isinstance(actions[0], exp.Delete):
2832            actions = self.expressions(expression, key="actions", flat=True)
2833        else:
2834            actions = self.expressions(expression, key="actions", flat=True)
2835
2836        exists = " IF EXISTS" if expression.args.get("exists") else ""
2837        only = " ONLY" if expression.args.get("only") else ""
2838        options = self.expressions(expression, key="options")
2839        options = f", {options}" if options else ""
2840        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
def add_column_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2842    def add_column_sql(self, expression: exp.AlterTable) -> str:
2843        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2844            return self.expressions(
2845                expression,
2846                key="actions",
2847                prefix="ADD COLUMN ",
2848            )
2849        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
2851    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2852        expressions = self.expressions(expression)
2853        exists = " IF EXISTS " if expression.args.get("exists") else " "
2854        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
2856    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2857        return f"ADD {self.expressions(expression)}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
2859    def distinct_sql(self, expression: exp.Distinct) -> str:
2860        this = self.expressions(expression, flat=True)
2861
2862        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2863            case = exp.case()
2864            for arg in expression.expressions:
2865                case = case.when(arg.is_(exp.null()), exp.null())
2866            this = self.sql(case.else_(f"({this})"))
2867
2868        this = f" {this}" if this else ""
2869
2870        on = self.sql(expression, "on")
2871        on = f" ON {on}" if on else ""
2872        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
2874    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2875        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
2877    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2878        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str:
2880    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2881        this_sql = self.sql(expression, "this")
2882        expression_sql = self.sql(expression, "expression")
2883        kind = "MAX" if expression.args.get("max") else "MIN"
2884        return f"{this_sql} HAVING {kind} {expression_sql}"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
2910    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2911        return self.sql(
2912            exp.Cast(
2913                this=exp.Div(this=expression.this, expression=expression.expression),
2914                to=exp.DataType(this=exp.DataType.Type.INT),
2915            )
2916        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
2918    def dpipe_sql(self, expression: exp.DPipe) -> str:
2919        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2920            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2921        return self.binary(expression, "||")
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
2923    def div_sql(self, expression: exp.Div) -> str:
2924        l, r = expression.left, expression.right
2925
2926        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2927            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2928
2929        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2930            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
2931                *exp.DataType.FLOAT_TYPES
2932            ):
2933                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2934
2935        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2936            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2937                return self.sql(
2938                    exp.cast(
2939                        l / r,
2940                        to=exp.DataType.Type.BIGINT,
2941                    )
2942                )
2943
2944        return self.binary(expression, "/")
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
2946    def overlaps_sql(self, expression: exp.Overlaps) -> str:
2947        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
2949    def distance_sql(self, expression: exp.Distance) -> str:
2950        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
2952    def dot_sql(self, expression: exp.Dot) -> str:
2953        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
2955    def eq_sql(self, expression: exp.EQ) -> str:
2956        return self.binary(expression, "=")
def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str:
2958    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
2959        return self.binary(expression, ":=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
2961    def escape_sql(self, expression: exp.Escape) -> str:
2962        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
2964    def glob_sql(self, expression: exp.Glob) -> str:
2965        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
2967    def gt_sql(self, expression: exp.GT) -> str:
2968        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
2970    def gte_sql(self, expression: exp.GTE) -> str:
2971        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
2973    def ilike_sql(self, expression: exp.ILike) -> str:
2974        return self.binary(expression, "ILIKE")
def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str:
2976    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
2977        return self.binary(expression, "ILIKE ANY")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
2979    def is_sql(self, expression: exp.Is) -> str:
2980        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
2981            return self.sql(
2982                expression.this if expression.expression.this else exp.not_(expression.this)
2983            )
2984        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
2986    def like_sql(self, expression: exp.Like) -> str:
2987        return self.binary(expression, "LIKE")
def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str:
2989    def likeany_sql(self, expression: exp.LikeAny) -> str:
2990        return self.binary(expression, "LIKE ANY")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
2992    def similarto_sql(self, expression: exp.SimilarTo) -> str:
2993        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
2995    def lt_sql(self, expression: exp.LT) -> str:
2996        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
2998    def lte_sql(self, expression: exp.LTE) -> str:
2999        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
3001    def mod_sql(self, expression: exp.Mod) -> str:
3002        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
3004    def mul_sql(self, expression: exp.Mul) -> str:
3005        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
3007    def neq_sql(self, expression: exp.NEQ) -> str:
3008        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
3010    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
3011        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
3013    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3014        return self.binary(expression, "IS DISTINCT FROM")
def or_sql(self, expression: sqlglot.expressions.Or) -> str:
3016    def or_sql(self, expression: exp.Or) -> str:
3017        return self.connector_sql(expression, "OR")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
3019    def slice_sql(self, expression: exp.Slice) -> str:
3020        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
3022    def sub_sql(self, expression: exp.Sub) -> str:
3023        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
3025    def trycast_sql(self, expression: exp.TryCast) -> str:
3026        return self.cast_sql(expression, safe_prefix="TRY_")
def log_sql(self, expression: sqlglot.expressions.Log) -> str:
3028    def log_sql(self, expression: exp.Log) -> str:
3029        this = expression.this
3030        expr = expression.expression
3031
3032        if not self.dialect.LOG_BASE_FIRST:
3033            this, expr = expr, this
3034
3035        return self.func("LOG", this, expr)
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
3037    def use_sql(self, expression: exp.Use) -> str:
3038        kind = self.sql(expression, "kind")
3039        kind = f" {kind}" if kind else ""
3040        this = self.sql(expression, "this")
3041        this = f" {this}" if this else ""
3042        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
3044    def binary(self, expression: exp.Binary, op: str) -> str:
3045        op = self.maybe_comment(op, comments=expression.comments)
3046        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
3048    def function_fallback_sql(self, expression: exp.Func) -> str:
3049        args = []
3050
3051        for key in expression.arg_types:
3052            arg_value = expression.args.get(key)
3053
3054            if isinstance(arg_value, list):
3055                for value in arg_value:
3056                    args.append(value)
3057            elif arg_value is not None:
3058                args.append(arg_value)
3059
3060        if self.normalize_functions:
3061            name = expression.sql_name()
3062        else:
3063            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3064
3065        return self.func(name, *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3067    def func(
3068        self,
3069        name: str,
3070        *args: t.Optional[exp.Expression | str],
3071        prefix: str = "(",
3072        suffix: str = ")",
3073    ) -> str:
3074        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
3076    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3077        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3078        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3079            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3080        return ", ".join(arg_sqls)
def text_width(self, args: Iterable) -> int:
3082    def text_width(self, args: t.Iterable) -> int:
3083        return sum(len(arg) for arg in args)
def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]:
3085    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3086        return format_time(
3087            self.sql(expression, "format"),
3088            self.dialect.INVERSE_TIME_MAPPING,
3089            self.dialect.INVERSE_TIME_TRIE,
3090        )
def expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[Collection[Union[str, sqlglot.expressions.Expression]]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, sep: str = ', ', prefix: str = '') -> str:
3092    def expressions(
3093        self,
3094        expression: t.Optional[exp.Expression] = None,
3095        key: t.Optional[str] = None,
3096        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3097        flat: bool = False,
3098        indent: bool = True,
3099        skip_first: bool = False,
3100        sep: str = ", ",
3101        prefix: str = "",
3102    ) -> str:
3103        expressions = expression.args.get(key or "expressions") if expression else sqls
3104
3105        if not expressions:
3106            return ""
3107
3108        if flat:
3109            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3110
3111        num_sqls = len(expressions)
3112
3113        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3114        pad = " " * self.pad
3115        stripped_sep = sep.strip()
3116
3117        result_sqls = []
3118        for i, e in enumerate(expressions):
3119            sql = self.sql(e, comment=False)
3120            if not sql:
3121                continue
3122
3123            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3124
3125            if self.pretty:
3126                if self.leading_comma:
3127                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3128                else:
3129                    result_sqls.append(
3130                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3131                    )
3132            else:
3133                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3134
3135        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3136        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
def op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3138    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3139        flat = flat or isinstance(expression.parent, exp.Properties)
3140        expressions_sql = self.expressions(expression, flat=flat)
3141        if flat:
3142            return f"{op} {expressions_sql}"
3143        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
3145    def naked_property(self, expression: exp.Property) -> str:
3146        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3147        if not property_name:
3148            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3149        return f"{property_name} {self.sql(expression, 'this')}"
def set_operation(self, expression: sqlglot.expressions.Union, op: str) -> str:
3151    def set_operation(self, expression: exp.Union, op: str) -> str:
3152        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
3153        op = self.seg(op)
3154        return self.query_modifiers(
3155            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
3156        )
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
3158    def tag_sql(self, expression: exp.Tag) -> str:
3159        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
3161    def token_sql(self, token_type: TokenType) -> str:
3162        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
3164    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3165        this = self.sql(expression, "this")
3166        expressions = self.no_identify(self.expressions, expression)
3167        expressions = (
3168            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3169        )
3170        return f"{this}{expressions}"
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
3172    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3173        this = self.sql(expression, "this")
3174        expressions = self.expressions(expression, flat=True)
3175        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
3177    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3178        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
3180    def when_sql(self, expression: exp.When) -> str:
3181        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3182        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3183        condition = self.sql(expression, "condition")
3184        condition = f" AND {condition}" if condition else ""
3185
3186        then_expression = expression.args.get("then")
3187        if isinstance(then_expression, exp.Insert):
3188            then = f"INSERT {self.sql(then_expression, 'this')}"
3189            if "expression" in then_expression.args:
3190                then += f" VALUES {self.sql(then_expression, 'expression')}"
3191        elif isinstance(then_expression, exp.Update):
3192            if isinstance(then_expression.args.get("expressions"), exp.Star):
3193                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3194            else:
3195                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3196        else:
3197            then = self.sql(then_expression)
3198        return f"WHEN {matched}{source}{condition} THEN {then}"
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
3200    def merge_sql(self, expression: exp.Merge) -> str:
3201        table = expression.this
3202        table_alias = ""
3203
3204        hints = table.args.get("hints")
3205        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3206            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3207            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3208
3209        this = self.sql(table)
3210        using = f"USING {self.sql(expression, 'using')}"
3211        on = f"ON {self.sql(expression, 'on')}"
3212        expressions = self.expressions(expression, sep=" ")
3213
3214        return self.prepend_ctes(
3215            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3216        )
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
3218    def tochar_sql(self, expression: exp.ToChar) -> str:
3219        if expression.args.get("format"):
3220            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3221
3222        return self.sql(exp.cast(expression.this, "text"))
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
3224    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3225        this = self.sql(expression, "this")
3226        kind = self.sql(expression, "kind")
3227        settings_sql = self.expressions(expression, key="settings", sep=" ")
3228        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3229        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
3231    def dictrange_sql(self, expression: exp.DictRange) -> str:
3232        this = self.sql(expression, "this")
3233        max = self.sql(expression, "max")
3234        min = self.sql(expression, "min")
3235        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
3237    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3238        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
3240    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3241        return ""
def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str:
3243    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3244        expressions = self.expressions(expression, key="expressions", flat=True)
3245        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3246        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3247        buckets = self.sql(expression, "buckets")
3248        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str:
3250    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3251        this = self.sql(expression, "this")
3252        having = self.sql(expression, "having")
3253
3254        if having:
3255            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3256
3257        return self.func("ANY_VALUE", this)
def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str:
3259    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3260        transform = self.func("TRANSFORM", *expression.expressions)
3261        row_format_before = self.sql(expression, "row_format_before")
3262        row_format_before = f" {row_format_before}" if row_format_before else ""
3263        record_writer = self.sql(expression, "record_writer")
3264        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3265        using = f" USING {self.sql(expression, 'command_script')}"
3266        schema = self.sql(expression, "schema")
3267        schema = f" AS {schema}" if schema else ""
3268        row_format_after = self.sql(expression, "row_format_after")
3269        row_format_after = f" {row_format_after}" if row_format_after else ""
3270        record_reader = self.sql(expression, "record_reader")
3271        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3272        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str:
3274    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3275        key_block_size = self.sql(expression, "key_block_size")
3276        if key_block_size:
3277            return f"KEY_BLOCK_SIZE = {key_block_size}"
3278
3279        using = self.sql(expression, "using")
3280        if using:
3281            return f"USING {using}"
3282
3283        parser = self.sql(expression, "parser")
3284        if parser:
3285            return f"WITH PARSER {parser}"
3286
3287        comment = self.sql(expression, "comment")
3288        if comment:
3289            return f"COMMENT {comment}"
3290
3291        visible = expression.args.get("visible")
3292        if visible is not None:
3293            return "VISIBLE" if visible else "INVISIBLE"
3294
3295        engine_attr = self.sql(expression, "engine_attr")
3296        if engine_attr:
3297            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3298
3299        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3300        if secondary_engine_attr:
3301            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3302
3303        self.unsupported("Unsupported index constraint option.")
3304        return ""
def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str:
3306    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3307        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3308        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str:
3310    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3311        kind = self.sql(expression, "kind")
3312        kind = f"{kind} INDEX" if kind else "INDEX"
3313        this = self.sql(expression, "this")
3314        this = f" {this}" if this else ""
3315        index_type = self.sql(expression, "index_type")
3316        index_type = f" USING {index_type}" if index_type else ""
3317        schema = self.sql(expression, "schema")
3318        schema = f" {schema}" if schema else ""
3319        options = self.expressions(expression, key="options", sep=" ")
3320        options = f" {options}" if options else ""
3321        return f"{kind}{this}{index_type}{schema}{options}"
def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str:
3323    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3324        if self.NVL2_SUPPORTED:
3325            return self.function_fallback_sql(expression)
3326
3327        case = exp.Case().when(
3328            expression.this.is_(exp.null()).not_(copy=False),
3329            expression.args["true"],
3330            copy=False,
3331        )
3332        else_cond = expression.args.get("false")
3333        if else_cond:
3334            case.else_(else_cond, copy=False)
3335
3336        return self.sql(case)
def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str:
3338    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3339        this = self.sql(expression, "this")
3340        expr = self.sql(expression, "expression")
3341        iterator = self.sql(expression, "iterator")
3342        condition = self.sql(expression, "condition")
3343        condition = f" IF {condition}" if condition else ""
3344        return f"{this} FOR {expr} IN {iterator}{condition}"
def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str:
3346    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3347        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str:
3349    def opclass_sql(self, expression: exp.Opclass) -> str:
3350        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def predict_sql(self, expression: sqlglot.expressions.Predict) -> str:
3352    def predict_sql(self, expression: exp.Predict) -> str:
3353        model = self.sql(expression, "this")
3354        model = f"MODEL {model}"
3355        table = self.sql(expression, "expression")
3356        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3357        parameters = self.sql(expression, "params_struct")
3358        return self.func("PREDICT", model, table, parameters or None)
def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str:
3360    def forin_sql(self, expression: exp.ForIn) -> str:
3361        this = self.sql(expression, "this")
3362        expression_sql = self.sql(expression, "expression")
3363        return f"FOR {this} DO {expression_sql}"
def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str:
3365    def refresh_sql(self, expression: exp.Refresh) -> str:
3366        this = self.sql(expression, "this")
3367        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3368        return f"REFRESH {table}{this}"
def operator_sql(self, expression: sqlglot.expressions.Operator) -> str:
3370    def operator_sql(self, expression: exp.Operator) -> str:
3371        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str:
3373    def toarray_sql(self, expression: exp.ToArray) -> str:
3374        arg = expression.this
3375        if not arg.type:
3376            from sqlglot.optimizer.annotate_types import annotate_types
3377
3378            arg = annotate_types(arg)
3379
3380        if arg.is_type(exp.DataType.Type.ARRAY):
3381            return self.sql(arg)
3382
3383        cond_for_null = arg.is_(exp.null())
3384        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:
3386    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3387        this = expression.this
3388        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3389            return self.sql(this)
3390
3391        return self.sql(exp.cast(this, "time"))
def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str:
3393    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3394        this = expression.this
3395        time_format = self.format_time(expression)
3396
3397        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3398            return self.sql(
3399                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3400            )
3401
3402        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3403            return self.sql(this)
3404
3405        return self.sql(exp.cast(this, "date"))
def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str:
3407    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3408        return self.sql(
3409            exp.func(
3410                "DATEDIFF",
3411                expression.this,
3412                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3413                "day",
3414            )
3415        )
def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str:
3417    def lastday_sql(self, expression: exp.LastDay) -> str:
3418        if self.LAST_DAY_SUPPORTS_DATE_PART:
3419            return self.function_fallback_sql(expression)
3420
3421        unit = expression.text("unit")
3422        if unit and unit != "MONTH":
3423            self.unsupported("Date parts are not supported in LAST_DAY.")
3424
3425        return self.func("LAST_DAY", expression.this)
def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str:
3427    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3428        if self.CAN_IMPLEMENT_ARRAY_ANY:
3429            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3430            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3431            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3432            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3433
3434        from sqlglot.dialects import Dialect
3435
3436        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3437        if self.dialect.__class__ != Dialect:
3438            self.unsupported("ARRAY_ANY is unsupported")
3439
3440        return self.function_fallback_sql(expression)
def generateseries_sql(self, expression: sqlglot.expressions.GenerateSeries) -> str:
3473    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
3474        expression.set("is_end_exclusive", None)
3475        return self.function_fallback_sql(expression)
def struct_sql(self, expression: sqlglot.expressions.Struct) -> str:
3477    def struct_sql(self, expression: exp.Struct) -> str:
3478        expression.set(
3479            "expressions",
3480            [
3481                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
3482                for e in expression.expressions
3483            ],
3484        )
3485
3486        return self.function_fallback_sql(expression)
def partitionrange_sql(self, expression: sqlglot.expressions.PartitionRange) -> str:
3488    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
3489        low = self.sql(expression, "this")
3490        high = self.sql(expression, "expression")
3491
3492        return f"{low} TO {high}"
def truncatetable_sql(self, expression: sqlglot.expressions.TruncateTable) -> str:
3494    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
3495        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
3496        tables = f" {self.expressions(expression)}"
3497
3498        exists = " IF EXISTS" if expression.args.get("exists") else ""
3499
3500        on_cluster = self.sql(expression, "cluster")
3501        on_cluster = f" {on_cluster}" if on_cluster else ""
3502
3503        identity = self.sql(expression, "identity")
3504        identity = f" {identity} IDENTITY" if identity else ""
3505
3506        option = self.sql(expression, "option")
3507        option = f" {option}" if option else ""
3508
3509        partition = self.sql(expression, "partition")
3510        partition = f" {partition}" if partition else ""
3511
3512        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"